1
2
3
4 package callgraph_test
5
6 import (
7 "log"
8 "sync"
9 "testing"
10
11 "golang.org/x/tools/go/callgraph"
12 "golang.org/x/tools/go/callgraph/cha"
13 "golang.org/x/tools/go/callgraph/rta"
14 "golang.org/x/tools/go/callgraph/static"
15 "golang.org/x/tools/go/callgraph/vta"
16 "golang.org/x/tools/go/loader"
17 "golang.org/x/tools/go/ssa"
18 "golang.org/x/tools/go/ssa/ssautil"
19 )
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 const httpEx = `package main
51
52 import (
53 "fmt"
54 "net/http"
55 )
56
57 func hello(w http.ResponseWriter, req *http.Request) {
58 fmt.Fprintf(w, "hello world\n")
59 }
60
61 func main() {
62 http.HandleFunc("/hello", hello)
63 http.ListenAndServe(":8090", nil)
64 }
65 `
66
67 var (
68 once sync.Once
69 prog *ssa.Program
70 main *ssa.Function
71 )
72
73 func example() (*ssa.Program, *ssa.Function) {
74 once.Do(func() {
75 var conf loader.Config
76 f, err := conf.ParseFile("<input>", httpEx)
77 if err != nil {
78 log.Fatal(err)
79 }
80 conf.CreateFromFiles(f.Name.Name, f)
81
82 lprog, err := conf.Load()
83 if err != nil {
84 log.Fatalf("test 'package %s': Load: %s", f.Name.Name, err)
85 }
86 prog = ssautil.CreateProgram(lprog, ssa.InstantiateGenerics)
87 prog.Build()
88
89 main = prog.Package(lprog.Created[0].Pkg).Members["main"].(*ssa.Function)
90 })
91 return prog, main
92 }
93
94 var stats bool = false
95
96 func logStats(b *testing.B, cnd bool, name string, cg *callgraph.Graph, main *ssa.Function) {
97 if cnd && stats {
98 e := 0
99 for _, n := range cg.Nodes {
100 e += len(n.Out)
101 }
102 r := len(reaches(main, cg, false))
103 b.Logf("%s:\t%d nodes\t%d edges\t%d reachable", name, len(cg.Nodes), e, r)
104 }
105 }
106
107 func BenchmarkStatic(b *testing.B) {
108 b.StopTimer()
109 prog, main := example()
110 b.StartTimer()
111
112 for i := 0; i < b.N; i++ {
113 cg := static.CallGraph(prog)
114 logStats(b, i == 0, "static", cg, main)
115 }
116 }
117
118 func BenchmarkCHA(b *testing.B) {
119 b.StopTimer()
120 prog, main := example()
121 b.StartTimer()
122
123 for i := 0; i < b.N; i++ {
124 cg := cha.CallGraph(prog)
125 logStats(b, i == 0, "cha", cg, main)
126 }
127 }
128
129 func BenchmarkRTA(b *testing.B) {
130 b.StopTimer()
131 _, main := example()
132 b.StartTimer()
133
134 for i := 0; i < b.N; i++ {
135 res := rta.Analyze([]*ssa.Function{main}, true)
136 cg := res.CallGraph
137 logStats(b, i == 0, "rta", cg, main)
138 }
139 }
140
141 func BenchmarkVTA(b *testing.B) {
142 b.StopTimer()
143 prog, main := example()
144 b.StartTimer()
145
146 for i := 0; i < b.N; i++ {
147 cg := vta.CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
148 logStats(b, i == 0, "vta", cg, main)
149 }
150 }
151
152 func BenchmarkVTA2(b *testing.B) {
153 b.StopTimer()
154 prog, main := example()
155 b.StartTimer()
156
157 for i := 0; i < b.N; i++ {
158 vta1 := vta.CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
159 cg := vta.CallGraph(reaches(main, vta1, true), vta1)
160 logStats(b, i == 0, "vta2", cg, main)
161 }
162 }
163
164 func BenchmarkVTA3(b *testing.B) {
165 b.StopTimer()
166 prog, main := example()
167 b.StartTimer()
168
169 for i := 0; i < b.N; i++ {
170 vta1 := vta.CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
171 vta2 := vta.CallGraph(reaches(main, vta1, true), vta1)
172 cg := vta.CallGraph(reaches(main, vta2, true), vta2)
173 logStats(b, i == 0, "vta3", cg, main)
174 }
175 }
176
177 func BenchmarkVTAAlt(b *testing.B) {
178 b.StopTimer()
179 prog, main := example()
180 b.StartTimer()
181
182 for i := 0; i < b.N; i++ {
183 cha := cha.CallGraph(prog)
184 cg := vta.CallGraph(reaches(main, cha, true), cha)
185 logStats(b, i == 0, "vta-alt", cg, main)
186 }
187 }
188
189 func BenchmarkVTAAlt2(b *testing.B) {
190 b.StopTimer()
191 prog, main := example()
192 b.StartTimer()
193
194 for i := 0; i < b.N; i++ {
195 cha := cha.CallGraph(prog)
196 vta1 := vta.CallGraph(reaches(main, cha, true), cha)
197 cg := vta.CallGraph(reaches(main, vta1, true), vta1)
198 logStats(b, i == 0, "vta-alt2", cg, main)
199 }
200 }
201
202
203
204
205 func reaches(source *ssa.Function, cg *callgraph.Graph, refs bool) map[*ssa.Function]bool {
206 seen := make(map[*ssa.Function]bool)
207 var visit func(f *ssa.Function)
208 visit = func(f *ssa.Function) {
209 if seen[f] {
210 return
211 }
212 seen[f] = true
213
214 if n := cg.Nodes[f]; n != nil {
215 for _, e := range n.Out {
216 if e.Site != nil {
217 visit(e.Callee.Func)
218 }
219 }
220 }
221
222 if refs {
223 var buf [10]*ssa.Value
224 for _, b := range f.Blocks {
225 for _, instr := range b.Instrs {
226 for _, op := range instr.Operands(buf[:0]) {
227 if fn, ok := (*op).(*ssa.Function); ok {
228 visit(fn)
229 }
230 }
231 }
232 }
233 }
234 }
235 visit(source)
236 return seen
237 }
238
View as plain text