-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
113 lines (100 loc) · 3.05 KB
/
main.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
106
107
108
109
110
111
112
113
package main
import (
"go/ast"
"go/parser"
"go/printer"
"go/token"
"io"
"io/ioutil"
"log"
"os"
"strings"
)
// Block represents the information about a basic block to be recorded in the analysis.
// Note: Our definition of basic block is based on control structures; we don't break
// apart && and ||. We could but it doesn't seem important enough to bother.
type Block struct {
startByte token.Pos
endByte token.Pos
numStmt int
}
// File is a wrapper for the state of a file used in the parser.
// The basic parse tree walker is a method of this type.
type File struct {
fset *token.FileSet
name string // Name of file.
astFile *ast.File
blocks []Block
atomicPkg string // Package name for "sync/atomic" in this file.
}
// Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
case *ast.FuncDecl:
//start := f.fset.Position(n.Pos())
//end := f.fset.Position(n.End())
// Prints information about functions
//fmt.Printf("name: %v start: %v:%v end: %v:%v\n", n.Name, start.Line, start.Column, end.Line, end.Column)
newList := []ast.Stmt{f.newCheckpoint("start")}
newList = append(newList, n.Body.List...)
n.Body.List = append(newList, f.newCheckpoint("stop"))
}
return f
}
func (f *File) newCheckpoint(kind string) ast.Stmt {
s := &ast.ExprStmt{
X: &ast.CallExpr{
Fun: &ast.Ident{
Name: kind,
},
Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: "\"" + kind + " measuring something...\""}},
},
}
return s
}
func main() {
// run.go_ is a copy of GOPATH$/src/github.com/spiffe/spire/cmd/spire-agent/cli/run/run.go
// to use as an example of how instrumentation could work.
// Basicly the code herein was copied from the cover golang tool and then adapted.
name := "run.go_"
fset := token.NewFileSet()
content, err := ioutil.ReadFile(name)
if err != nil {
log.Fatalf("cover: %s: %s", name, err)
}
parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments)
if err != nil {
log.Fatalf("cover: %s: %s", name, err)
}
parsedFile.Comments = trimComments(parsedFile, fset)
file := &File{
fset: fset,
name: name,
astFile: parsedFile,
}
ast.Walk(file, file.astFile)
fd := os.Stdout
file.print(fd)
}
func (f *File) print(w io.Writer) {
printer.Fprint(w, f.fset, f.astFile)
}
// trimComments drops all but the //go: comments, some of which are semantically important.
// We drop all others because they can appear in places that cause our counters
// to appear in syntactically incorrect places. //go: appears at the beginning of
// the line and is syntactically safe.
func trimComments(file *ast.File, fset *token.FileSet) []*ast.CommentGroup {
var comments []*ast.CommentGroup
for _, group := range file.Comments {
var list []*ast.Comment
for _, comment := range group.List {
if strings.HasPrefix(comment.Text, "//go:") && fset.Position(comment.Slash).Column == 1 {
list = append(list, comment)
}
}
if list != nil {
comments = append(comments, &ast.CommentGroup{List: list})
}
}
return comments
}