func checkCallsExpectation(prog *ssa.Program, e *expectation, cg *callgraph.Graph) bool { found := make(map[string]int) err := callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error { // Name-based matching is inefficient but it allows us to // match functions whose names that would not appear in an // index ("<root>") or which are not unique ("[email protected]"). if edge.Caller.Func.String() == e.args[0] { calleeStr := edge.Callee.Func.String() if calleeStr == e.args[1] { return errOK // expectation satisified; stop the search } found[calleeStr]++ } return nil }) if err == errOK { return true } if len(found) == 0 { e.errorf("didn't find any calls from %s", e.args[0]) } e.errorf("found no call from %s to %s, but only to %s", e.args[0], e.args[1], join(found)) return false }
func printGraph(cg *callgraph.Graph, from *types.Package) string { var edges []string callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error { if strings.Contains(e.Description(), "dynamic") { edges = append(edges, fmt.Sprintf("%s --> %s", e.Caller.Func.RelString(from), e.Callee.Func.RelString(from))) } return nil }) sort.Strings(edges) var buf bytes.Buffer buf.WriteString("Dynamic calls\n") for _, edge := range edges { fmt.Fprintf(&buf, " %s\n", edge) } return strings.TrimSpace(buf.String()) }
func printResult(res *rta.Result, from *types.Package) string { var buf bytes.Buffer writeSorted := func(ss []string) { sort.Strings(ss) for _, s := range ss { fmt.Fprintf(&buf, " %s\n", s) } } buf.WriteString("Dynamic calls\n") var edges []string callgraph.GraphVisitEdges(res.CallGraph, func(e *callgraph.Edge) error { if strings.Contains(e.Description(), "dynamic") { edges = append(edges, fmt.Sprintf("%s --> %s", e.Caller.Func.RelString(from), e.Callee.Func.RelString(from))) } return nil }) writeSorted(edges) buf.WriteString("Reachable functions\n") var reachable []string for f := range res.Reachable { reachable = append(reachable, f.RelString(from)) } writeSorted(reachable) buf.WriteString("Reflect types\n") var rtypes []string res.RuntimeTypes.Iterate(func(key types.Type, value interface{}) { if value == false { // accessible to reflection rtypes = append(rtypes, types.TypeString(from, key)) } }) writeSorted(rtypes) return strings.TrimSpace(buf.String()) }
func TestStatic(t *testing.T) { conf := loader.Config{ParserMode: parser.ParseComments} f, err := conf.ParseFile("P.go", input) if err != nil { t.Fatal(err) } conf.CreateFromFiles("P", f) iprog, err := conf.Load() if err != nil { t.Fatal(err) } P := iprog.Created[0].Pkg prog := ssa.Create(iprog, 0) prog.BuildAll() cg := static.CallGraph(prog) var edges []string callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error { edges = append(edges, fmt.Sprintf("%s -> %s", e.Caller.Func.RelString(P), e.Callee.Func.RelString(P))) return nil }) sort.Strings(edges) want := []string{ "(*C).f -> (C).f", "f -> (C).f", "f -> f$1", "f -> g", } if !reflect.DeepEqual(edges, want) { t.Errorf("Got edges %v, want %v", edges, want) } }
// This program demonstrates how to use the pointer analysis to // obtain a conservative call-graph of a Go program. // It also shows how to compute the points-to set of a variable, // in this case, (C).f's ch parameter. // func Example() { const myprog = ` package main import "fmt" type I interface { f(map[string]int) } type C struct{} func (C) f(m map[string]int) { fmt.Println("C.f()") } func main() { var i I = C{} x := map[string]int{"one":1} i.f(x) // dynamic method call } ` var conf loader.Config // Parse the input file, a string. // (Command-line tools should use conf.FromArgs.) file, err := conf.ParseFile("myprog.go", myprog) if err != nil { fmt.Print(err) // parse error return } // Create single-file main package and import its dependencies. conf.CreateFromFiles("main", file) iprog, err := conf.Load() if err != nil { fmt.Print(err) // type error in some package return } // Create SSA-form program representation. prog := ssa.Create(iprog, 0) mainPkg := prog.Package(iprog.Created[0].Pkg) // Build SSA code for bodies of all functions in the whole program. prog.BuildAll() // Configure the pointer analysis to build a call-graph. config := &pointer.Config{ Mains: []*ssa.Package{mainPkg}, BuildCallGraph: true, } // Query points-to set of (C).f's parameter m, a map. C := mainPkg.Type("C").Type() Cfm := prog.LookupMethod(C, mainPkg.Object, "f").Params[1] config.AddQuery(Cfm) // Run the pointer analysis. result, err := pointer.Analyze(config) if err != nil { panic(err) // internal error in pointer analysis } // Find edges originating from the main package. // By converting to strings, we de-duplicate nodes // representing the same function due to context sensitivity. var edges []string callgraph.GraphVisitEdges(result.CallGraph, func(edge *callgraph.Edge) error { caller := edge.Caller.Func if caller.Pkg == mainPkg { edges = append(edges, fmt.Sprint(caller, " --> ", edge.Callee.Func)) } return nil }) // Print the edges in sorted order. sort.Strings(edges) for _, edge := range edges { fmt.Println(edge) } fmt.Println() // Print the labels of (C).f(m)'s points-to set. fmt.Println("m may point to:") var labels []string for _, l := range result.Queries[Cfm].PointsTo().Labels() { label := fmt.Sprintf(" %s: %s", prog.Fset.Position(l.Pos()), l) labels = append(labels, label) } sort.Strings(labels) for _, label := range labels { fmt.Println(label) } // Output: // (main.C).f --> fmt.Println // main.init --> fmt.init // main.main --> (main.C).f // // m may point to: // myprog.go:18:21: makemap }