/
analysis.go
105 lines (94 loc) · 2.4 KB
/
analysis.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package analysis
import (
"bytes"
"errors"
"fmt"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/callgraph/static"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
"log"
)
type ProgramAnalysis struct {
Prog *ssa.Program
Pkgs []*ssa.Package
Mains []*ssa.Package
}
const pkgLoadMode = packages.NeedName |
packages.NeedFiles |
packages.NeedCompiledGoFiles |
packages.NeedImports |
packages.NeedDeps |
packages.NeedExportsFile |
packages.NeedTypes |
packages.NeedSyntax |
packages.NeedTypesInfo |
packages.NeedTypesSizes
type AnalysisMode uint64
const (
PointerAnalysis AnalysisMode = iota
StaticAnalysis
ClassHierarchyAnalysis
RapidTypeAnalysis
)
func RunAnalysis(withTests bool, buildFlags []string, pkgPatterns []string) (*ProgramAnalysis, error) {
conf := &packages.Config{
Mode: pkgLoadMode,
Tests: withTests,
BuildFlags: buildFlags,
}
loaded, err := packages.Load(conf, pkgPatterns...)
if err != nil {
log.Fatalln("failed packages load:", err)
}
prog, initialPkgs := ssautil.Packages(loaded, 0)
var errorMsg bytes.Buffer
for i, p := range initialPkgs {
if p == nil && loaded[i].Name != "" {
errorMsg.WriteString("failed to get SSA for pkg: ")
errorMsg.WriteString(loaded[i].PkgPath)
errorMsg.WriteString("\n")
}
}
if errorMsg.Len() != 0 {
return nil, errors.New(errorMsg.String())
}
prog.Build()
pkgs := prog.AllPackages()
mains := ssautil.MainPackages(pkgs)
return &ProgramAnalysis{
Prog: prog,
Pkgs: pkgs,
Mains: mains,
}, nil
}
func (mode AnalysisMode) ComputeCallgraph(data *ProgramAnalysis) *callgraph.Graph {
switch mode {
case PointerAnalysis:
ptrcfg := &pointer.Config{
Mains: data.Mains,
BuildCallGraph: true,
}
result, err := pointer.Analyze(ptrcfg)
if err != nil { // not a user-input problem if it fails, see Analyze doc.
panic(fmt.Errorf("pointer analysis failed %v", err))
}
return result.CallGraph
case StaticAnalysis:
return static.CallGraph(data.Prog)
case ClassHierarchyAnalysis:
return cha.CallGraph(data.Prog)
case RapidTypeAnalysis:
var roots []*ssa.Function
for _, m := range data.Mains {
roots = append(roots, m.Func("init"), m.Func("main"))
}
return rta.Analyze(roots, true).CallGraph
default:
return nil
}
}