Go-Callvis Viewer

Home|gocallvis/analysis.go
1package main
2
3import (
4    "errors"
5    "fmt"
6    "go/build"
7    "go/types"
8    "io"
9    "log"
10    "net/http"
11    "os"
12    "path/filepath"
13    "strings"
14
15    "golang.org/x/tools/go/callgraph"
16    "golang.org/x/tools/go/callgraph/cha"
17    "golang.org/x/tools/go/callgraph/rta"
18    "golang.org/x/tools/go/callgraph/static"
19
20    "golang.org/x/tools/go/packages"
21    "golang.org/x/tools/go/pointer"
22    "golang.org/x/tools/go/ssa"
23    "golang.org/x/tools/go/ssa/ssautil"
24)
25
26type CallGraphType string
27
28const (
29    CallGraphTypeStatic                = "static"
30    CallGraphTypeCha                   = "cha"
31    CallGraphTypeRta                   = "rta"
32    CallGraphTypePointer CallGraphType = "pointer"
33)
34
35//==[ type def/func: analysis   ]===============================================
36type renderOpts struct {
37    cacheDir string
38    focus    string
39    group    []string
40    ignore   []string
41    include  []string
42    limit    []string
43    nointer  bool
44    refresh  bool
45    nostd    bool
46    algo     CallGraphType
47}
48
49// mainPackages returns the main packages to analyze.
50// Each resulting package is named "main" and has a main function.
51func mainPackages(pkgs []*ssa.Package) ([]*ssa.Packageerror) {
52    var mains []*ssa.Package
53    for _p := range pkgs {
54        if p != nil && p.Pkg.Name() == "main" && p.Func("main") != nil {
55            mains = append(mainsp)
56        }
57    }
58    if len(mains) == 0 {
59        return nilfmt.Errorf("no main packages")
60    }
61    return mainsnil
62}
63
64//==[ type def/func: analysis   ]===============================================
65type analysis struct {
66    opts      *renderOpts
67    prog      *ssa.Program
68    pkgs      []*ssa.Package
69    mainPkg   *ssa.Package
70    callgraph *callgraph.Graph
71}
72
73var Analysis *analysis
74
75func (a *analysisDoAnalysis(
76    algo CallGraphType,
77    dir string,
78    tests bool,
79    args []string,
80error {
81    cfg := &packages.Config{
82        Mode:       packages.LoadAllSyntax,
83        Tests:      tests,
84        Dir:        dir,
85        BuildFlagsbuild.Default.BuildTags,
86    }
87
88    initialerr := packages.Load(cfgargs...)
89    if err != nil {
90        return err
91    }
92
93    if packages.PrintErrors(initial) > 0 {
94        return fmt.Errorf("packages contain errors")
95    }
96
97    // Create and build SSA-form program representation.
98    progpkgs := ssautil.AllPackages(initial0)
99    prog.Build()
100
101    var graph *callgraph.Graph
102    var mainPkg *ssa.Package
103
104    switch algo {
105    case CallGraphTypeStatic:
106        graph = static.CallGraph(prog)
107    case CallGraphTypeCha:
108        graph = cha.CallGraph(prog)
109    case CallGraphTypeRta:
110        mainserr := mainPackages(prog.AllPackages())
111        if err != nil {
112            return err
113        }
114        var roots []*ssa.Function
115        mainPkg = mains[0]
116        for _main := range mains {
117            roots = append(rootsmain.Func("main"))
118        }
119        graph = rta.Analyze(rootstrue).CallGraph
120    case CallGraphTypePointer:
121        mainserr := mainPackages(prog.AllPackages())
122        if err != nil {
123            return err
124        }
125        mainPkg = mains[0]
126        config := &pointer.Config{
127            Mains:          mains,
128            BuildCallGraphtrue,
129        }
130        ptareserr := pointer.Analyze(config)
131        if err != nil {
132            return err
133        }
134        graph = ptares.CallGraph
135    default:
136        return fmt.Errorf("invalid call graph type: %s"a.opts.algo)
137    }
138
139    //cg.DeleteSyntheticNodes()
140
141    a.prog = prog
142    a.pkgs = pkgs
143    a.mainPkg = mainPkg
144    a.callgraph = graph
145    return nil
146}
147
148func (a *analysisOptsSetup() {
149    a.opts = &renderOpts{
150        cacheDir: *cacheDir,
151        focus:    *focusFlag,
152        group:    []string{*groupFlag},
153        ignore:   []string{*ignoreFlag},
154        include:  []string{*includeFlag},
155        limit:    []string{*limitFlag},
156        nointer:  *nointerFlag,
157        nostd:    *nostdFlag,
158    }
159}
160
161func (a *analysisProcessListArgs() (e error) {
162    var groupBy []string
163    var ignorePaths []string
164    var includePaths []string
165    var limitPaths []string
166
167    for _g := range strings.Split(a.opts.group[0], ",") {
168        g := strings.TrimSpace(g)
169        if g == "" {
170            continue
171        }
172        if g != "pkg" && g != "type" {
173            e = errors.New("invalid group option")
174            return
175        }
176        groupBy = append(groupByg)
177    }
178
179    for _p := range strings.Split(a.opts.ignore[0], ",") {
180        p = strings.TrimSpace(p)
181        if p != "" {
182            ignorePaths = append(ignorePathsp)
183        }
184    }
185
186    for _p := range strings.Split(a.opts.include[0], ",") {
187        p = strings.TrimSpace(p)
188        if p != "" {
189            includePaths = append(includePathsp)
190        }
191    }
192
193    for _p := range strings.Split(a.opts.limit[0], ",") {
194        p = strings.TrimSpace(p)
195        if p != "" {
196            limitPaths = append(limitPathsp)
197        }
198    }
199
200    a.opts.group = groupBy
201    a.opts.ignore = ignorePaths
202    a.opts.include = includePaths
203    a.opts.limit = limitPaths
204
205    return
206}
207
208func (a *analysisOverrideByHTTP(r *http.Request) {
209    if f := r.FormValue("f"); f == "all" {
210        a.opts.focus = ""
211    } else if f != "" {
212        a.opts.focus = f
213    }
214    if std := r.FormValue("std"); std != "" {
215        a.opts.nostd = false
216    }
217    if inter := r.FormValue("nointer"); inter != "" {
218        a.opts.nointer = true
219    }
220    if refresh := r.FormValue("refresh"); refresh != "" {
221        a.opts.refresh = true
222    }
223    if g := r.FormValue("group"); g != "" {
224        a.opts.group[0] = g
225    }
226    if l := r.FormValue("limit"); l != "" {
227        a.opts.limit[0] = l
228    }
229    if ign := r.FormValue("ignore"); ign != "" {
230        a.opts.ignore[0] = ign
231    }
232    if inc := r.FormValue("include"); inc != "" {
233        a.opts.include[0] = inc
234    }
235    return
236}
237
238// basically do printOutput() with previously checking
239// focus option and respective package
240func (a *analysisRender() ([]byteerror) {
241    var (
242        err      error
243        ssaPkg   *ssa.Package
244        focusPkg *types.Package
245    )
246
247    if a.opts.focus != "" {
248        if ssaPkg = a.prog.ImportedPackage(a.opts.focus); ssaPkg == nil {
249            if strings.Contains(a.opts.focus"/") {
250                return nilfmt.Errorf("focus failed: %v"err)
251            }
252            // try to find package by name
253            var foundPaths []string
254            for _p := range a.pkgs {
255                if p.Pkg.Name() == a.opts.focus {
256                    foundPaths = append(foundPathsp.Pkg.Path())
257                }
258            }
259            if len(foundPaths) == 0 {
260                return nilfmt.Errorf("focus failed, could not find package: %v"a.opts.focus)
261            } else if len(foundPaths) > 1 {
262                for _p := range foundPaths {
263                    fmt.Fprintf(os.Stderr" - %s\n"p)
264                }
265                return nilfmt.Errorf("focus failed, found multiple packages with name: %v"a.opts.focus)
266            }
267            // found single package
268            if ssaPkg = a.prog.ImportedPackage(foundPaths[0]); ssaPkg == nil {
269                return nilfmt.Errorf("focus failed: %v"err)
270            }
271        }
272        focusPkg = ssaPkg.Pkg
273        logf("focusing: %v"focusPkg.Path())
274    }
275
276    doterr := printOutput(
277        a.prog,
278        a.mainPkg,
279        a.callgraph,
280        focusPkg,
281        a.opts.limit,
282        a.opts.ignore,
283        a.opts.include,
284        a.opts.group,
285        a.opts.nostd,
286        a.opts.nointer,
287    )
288    if err != nil {
289        return nilfmt.Errorf("processing failed: %v"err)
290    }
291
292    return dotnil
293}
294
295func (a *analysisFindCachedImg() string {
296    if a.opts.cacheDir == "" || a.opts.refresh {
297        return ""
298    }
299
300    focus := a.opts.focus
301    if focus == "" {
302        focus = "all"
303    }
304    focusFilePath := focus + "." + *outputFormat
305    absFilePath := filepath.Join(a.opts.cacheDirfocusFilePath)
306
307    if existserr := pathExists(absFilePath); err != nil || !exists {
308        log.Println("not cached img:"absFilePath)
309        return ""
310    }
311
312    log.Println("hit cached img")
313    return absFilePath
314}
315
316func (a *analysisCacheImg(img stringerror {
317    if a.opts.cacheDir == "" || img == "" {
318        return nil
319    }
320
321    focus := a.opts.focus
322    if focus == "" {
323        focus = "all"
324    }
325    absCacheDirPrefix := filepath.Join(a.opts.cacheDirfocus)
326    absCacheDirPath := strings.TrimRightFunc(absCacheDirPrefix, func(r runebool {
327        return r != '\\' && r != '/'
328    })
329    err := os.MkdirAll(absCacheDirPathos.ModePerm)
330    if err != nil {
331        return err
332    }
333
334    absFilePath := absCacheDirPrefix + "." + *outputFormat
335    _err = copyFile(imgabsFilePath)
336    if err != nil {
337        return err
338    }
339
340    return nil
341}
342
343func pathExists(path string) (boolerror) {
344    _err := os.Stat(path)
345    if err == nil {
346        return truenil
347    }
348    if os.IsNotExist(err) {
349        return falsenil
350    }
351    return falseerr
352}
353
354func copyFile(srcdst string) (int64error) {
355    sourceFileStaterr := os.Stat(src)
356
357    if err != nil {
358        return 0err
359    }
360
361    if !sourceFileStat.Mode().IsRegular() {
362        return 0fmt.Errorf("%s is not a regular file"src)
363    }
364
365    sourceerr := os.Open(src)
366    if err != nil {
367        return 0err
368    }
369    defer source.Close()
370
371    destinationerr := os.Create(dst)
372    if err != nil {
373        return 0err
374    }
375    defer destination.Close()
376    nByteserr := io.Copy(destinationsource)
377    return nByteserr
378}
379
MembersX
analysis.ProcessListArgs.limitPaths
analysis.CacheImg.img
packages
CallGraphTypeCha
copyFile.dst
analysis.FindCachedImg.focus
analysis.CacheImg.focus
pathExists.err
copyFile
filepath
renderOpts.limit
analysis.Render.BlockStmt.BlockStmt.BlockStmt.RangeStmt_5823.p
CallGraphTypeRta
analysis.ProcessListArgs.a
analysis.FindCachedImg.absFilePath
analysis.DoAnalysis
analysis.DoAnalysis.args
analysis.DoAnalysis.pkgs
analysis.OverrideByHTTP.inter
analysis.OverrideByHTTP.ign
renderOpts.group
renderOpts.nointer
mainPackages.RangeStmt_1139.p
analysis.pkgs
analysis.Render.focusPkg
analysis.OverrideByHTTP.refresh
analysis.CacheImg
types
renderOpts.refresh
analysis.OverrideByHTTP.l
build
analysis.callgraph
analysis.DoAnalysis.BlockStmt.ptares
analysis.ProcessListArgs
analysis.DoAnalysis.BlockStmt.err
analysis.ProcessListArgs.e
analysis.ProcessListArgs.includePaths
analysis.Render.ssaPkg
pathExists
renderOpts.ignore
analysis.mainPkg
analysis.DoAnalysis.a
copyFile.source
analysis.ProcessListArgs.RangeStmt_3583.BlockStmt.g
analysis.Render.dot
pathExists._
ssa
analysis.DoAnalysis.dir
analysis.DoAnalysis.BlockStmt.RangeStmt_2495.main
analysis.ProcessListArgs.RangeStmt_3971.p
copyFile.src
pointer
analysis.DoAnalysis.algo
analysis.ProcessListArgs.groupBy
copyFile.nBytes
fmt
analysis.OverrideByHTTP.std
analysis.Render.BlockStmt.BlockStmt.foundPaths
analysis.OverrideByHTTP.g
log
analysis.ProcessListArgs.ignorePaths
analysis.FindCachedImg.a
analysis.OverrideByHTTP.r
analysis.OverrideByHTTP.f
analysis.Render.err
CallGraphType
renderOpts.cacheDir
analysis.DoAnalysis.cfg
CallGraphTypePointer
analysis.DoAnalysis.mainPkg
analysis.ProcessListArgs.RangeStmt_4122.p
analysis.prog
analysis.OptsSetup
errors
analysis.OverrideByHTTP.a
analysis.CacheImg.err
cha
ssautil
renderOpts.algo
analysis.CacheImg.absCacheDirPrefix
strings
analysis.ProcessListArgs.RangeStmt_3583.g
analysis.CacheImg.a
analysis.FindCachedImg.err
analysis.OverrideByHTTP
analysis.Render.BlockStmt.BlockStmt.RangeStmt_5543.p
analysis.FindCachedImg.exists
analysis.DoAnalysis.BlockStmt.mains
analysis.DoAnalysis.BlockStmt.config
analysis.OptsSetup.a
analysis.CacheImg.absCacheDirPath
pathExists.path
mainPackages.mains
Analysis
analysis.DoAnalysis.initial
copyFile.destination
analysis.Render.a
static
renderOpts.nostd
analysis.ProcessListArgs.RangeStmt_3823.p
io
analysis.FindCachedImg
renderOpts.include
analysis.opts
analysis.Render
CallGraphTypeStatic
mainPackages
analysis.DoAnalysis.prog
analysis.OverrideByHTTP.inc
http
os
callgraph
mainPackages.pkgs
analysis.DoAnalysis.graph
analysis.DoAnalysis.BlockStmt.roots
copyFile.err
rta
renderOpts
renderOpts.focus
copyFile.sourceFileStat
analysis
analysis.DoAnalysis.tests
analysis.DoAnalysis.err
Members
X