GoPLS Viewer

Home|gopls/cmd/guru/callstack.go
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
5package main
6
7import (
8    "fmt"
9    "go/token"
10
11    "golang.org/x/tools/cmd/guru/serial"
12    "golang.org/x/tools/go/callgraph"
13    "golang.org/x/tools/go/callgraph/static"
14    "golang.org/x/tools/go/loader"
15    "golang.org/x/tools/go/ssa"
16    "golang.org/x/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.
28func callstack(q *Queryerror {
29    fset := token.NewFileSet()
30    lconf := loader.Config{FsetfsetBuildq.Build}
31
32    if err := setPTAScope(&lconfq.Scope); err != nil {
33        return err
34    }
35
36    // Load/parse/type-check the program.
37    lprogerr := loadWithSoftErrors(&lconf)
38    if err != nil {
39        return err
40    }
41
42    qposerr := parseQueryPos(lprogq.Posfalse)
43    if err != nil {
44        return err
45    }
46
47    prog := ssautil.CreateProgram(lprog0)
48
49    ptaConfigerr := setupPTA(proglprogq.PTALogq.Reflection)
50    if err != nil {
51        return err
52    }
53
54    pkg := prog.Package(qpos.info.Pkg)
55    if pkg == nil {
56        return fmt.Errorf("no SSA package")
57    }
58
59    if !ssa.HasEnclosingFunction(pkgqpos.path) {
60        return fmt.Errorf("this position is not inside a function")
61    }
62
63    // Defer SSA construction till after errors are reported.
64    prog.Build()
65
66    target := ssa.EnclosingFunction(pkgqpos.path)
67    if target == nil {
68        return fmt.Errorf("no SSA function built for this location (dead code?)")
69    }
70
71    var callpath []*callgraph.Edge
72    isEnd := func(n *callgraph.Nodebool { return n.Func == target }
73
74    // First, build a callgraph containing only static call edges,
75    // and search for an arbitrary path from a root to the target function.
76    // This is quick, and the user wants a static path if one exists.
77    cg := static.CallGraph(prog)
78    cg.DeleteSyntheticNodes()
79    for _ep := range entryPoints(ptaConfig.Mains) {
80        callpath = callgraph.PathSearch(cg.CreateNode(ep), isEnd)
81        if callpath != nil {
82            break
83        }
84    }
85
86    // No fully static path found.
87    // Run the pointer analysis and build a complete call graph.
88    if callpath == nil {
89        ptaConfig.BuildCallGraph = true
90        cg := ptrAnalysis(ptaConfig).CallGraph
91        cg.DeleteSyntheticNodes()
92        callpath = callgraph.PathSearch(cg.RootisEnd)
93        if callpath != nil {
94            callpath = callpath[1:] // remove synthetic edge from <root>
95        }
96    }
97
98    q.Output(fset, &callstackResult{
99        qpos:     qpos,
100        target:   target,
101        callpathcallpath,
102    })
103    return nil
104}
105
106type callstackResult struct {
107    qpos     *queryPos
108    target   *ssa.Function
109    callpath []*callgraph.Edge
110}
111
112func (r *callstackResultPrintPlain(printf printfFunc) {
113    if r.callpath != nil {
114        printf(r.qpos"Found a call path from root to %s"r.target)
115        printf(r.target"%s"r.target)
116        for i := len(r.callpath) - 1i >= 0i-- {
117            edge := r.callpath[i]
118            printf(edge"%s from %s"edge.Description(), edge.Caller.Func)
119        }
120    } else {
121        printf(r.target"%s is unreachable in this analysis scope"r.target)
122    }
123}
124
125func (r *callstackResultJSON(fset *token.FileSet) []byte {
126    var callers []serial.Caller
127    for i := len(r.callpath) - 1i >= 0i-- { // (innermost first)
128        edge := r.callpath[i]
129        callers = append(callersserial.Caller{
130            Pos:    fset.Position(edge.Pos()).String(),
131            Calleredge.Caller.Func.String(),
132            Desc:   edge.Description(),
133        })
134    }
135    return toJSON(&serial.CallStack{
136        Pos:     fset.Position(r.target.Pos()).String(),
137        Target:  r.target.String(),
138        Callerscallers,
139    })
140}
141
MembersX
callstackResult
callstackResult.JSON
callstackResult.JSON.fset
callstack.lprog
callstack.err
callstackResult.PrintPlain.r
callstackResult.JSON.r
callstack.fset
callstack.lconf
callstack.ptaConfig
callstack.RangeStmt_2165.ep
callstackResult.target
callstackResult.callpath
callstackResult.JSON.callers
callstack.q
callstack.qpos
callstack.pkg
callstack.callpath
callstack.BlockStmt.cg
callstack
callstack.cg
callstack.prog
callstackResult.PrintPlain
callstackResult.PrintPlain.printf
static
callstackResult.qpos
callstack.target
Members
X