github.com/april1989/origin-go-tools@v0.0.32/cmd/guru/callstack.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "fmt" 9 "go/token" 10 11 "github.com/april1989/origin-go-tools/cmd/guru/serial" 12 "github.com/april1989/origin-go-tools/go/callgraph" 13 "github.com/april1989/origin-go-tools/go/callgraph/static" 14 "github.com/april1989/origin-go-tools/go/loader" 15 "github.com/april1989/origin-go-tools/go/ssa" 16 "github.com/april1989/origin-go-tools/go/ssa/ssautil" 17 ) 18 19 // The callstack function displays an arbitrary path from a root of the callgraph 20 // to the function at the current position. 21 // 22 // The information may be misleading in a context-insensitive 23 // analysis. e.g. the call path X->Y->Z might be infeasible if Y never 24 // calls Z when it is called from X. TODO(adonovan): think about UI. 25 // 26 // TODO(adonovan): permit user to specify a starting point other than 27 // the analysis root. 28 // 29 func callstack(q *Query) error { 30 fset := token.NewFileSet() 31 lconf := loader.Config{Fset: fset, Build: q.Build} 32 33 if err := setPTAScope(&lconf, q.Scope); err != nil { 34 return err 35 } 36 37 // Load/parse/type-check the program. 38 lprog, err := loadWithSoftErrors(&lconf) 39 if err != nil { 40 return err 41 } 42 43 qpos, err := parseQueryPos(lprog, q.Pos, false) 44 if err != nil { 45 return err 46 } 47 48 prog := ssautil.CreateProgram(lprog, 0) 49 50 ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) 51 if err != nil { 52 return err 53 } 54 55 pkg := prog.Package(qpos.info.Pkg) 56 if pkg == nil { 57 return fmt.Errorf("no SSA package") 58 } 59 60 if !ssa.HasEnclosingFunction(pkg, qpos.path) { 61 return fmt.Errorf("this position is not inside a function") 62 } 63 64 // Defer SSA construction till after errors are reported. 65 prog.Build() 66 67 target := ssa.EnclosingFunction(pkg, qpos.path) 68 if target == nil { 69 return fmt.Errorf("no SSA function built for this location (dead code?)") 70 } 71 72 var callpath []*callgraph.Edge 73 isEnd := func(n *callgraph.Node) bool { return n.Func == target } 74 75 // First, build a callgraph containing only static call edges, 76 // and search for an arbitrary path from a root to the target function. 77 // This is quick, and the user wants a static path if one exists. 78 cg := static.CallGraph(prog) 79 cg.DeleteSyntheticNodes() 80 for _, ep := range entryPoints(ptaConfig.Mains) { 81 callpath = callgraph.PathSearch(cg.CreateNode(ep), isEnd) 82 if callpath != nil { 83 break 84 } 85 } 86 87 // No fully static path found. 88 // Run the pointer analysis and build a complete call graph. 89 if callpath == nil { 90 ptaConfig.BuildCallGraph = true 91 cg := ptrAnalysis(ptaConfig).CallGraph 92 cg.DeleteSyntheticNodes() 93 callpath = callgraph.PathSearch(cg.Root, isEnd) 94 if callpath != nil { 95 callpath = callpath[1:] // remove synthetic edge from <root> 96 } 97 } 98 99 q.Output(fset, &callstackResult{ 100 qpos: qpos, 101 target: target, 102 callpath: callpath, 103 }) 104 return nil 105 } 106 107 type callstackResult struct { 108 qpos *queryPos 109 target *ssa.Function 110 callpath []*callgraph.Edge 111 } 112 113 func (r *callstackResult) PrintPlain(printf printfFunc) { 114 if r.callpath != nil { 115 printf(r.qpos, "Found a call path from root to %s", r.target) 116 printf(r.target, "%s", r.target) 117 for i := len(r.callpath) - 1; i >= 0; i-- { 118 edge := r.callpath[i] 119 printf(edge, "%s from %s", edge.Description(), edge.Caller.Func) 120 } 121 } else { 122 printf(r.target, "%s is unreachable in this analysis scope", r.target) 123 } 124 } 125 126 func (r *callstackResult) JSON(fset *token.FileSet) []byte { 127 var callers []serial.Caller 128 for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first) 129 edge := r.callpath[i] 130 callers = append(callers, serial.Caller{ 131 Pos: fset.Position(edge.Pos()).String(), 132 Caller: edge.Caller.Func.String(), 133 Desc: edge.Description(), 134 }) 135 } 136 return toJSON(&serial.CallStack{ 137 Pos: fset.Position(r.target.Pos()).String(), 138 Target: r.target.String(), 139 Callers: callers, 140 }) 141 }