diff --git a/instrgen/driver/main.go b/instrgen/driver/main.go index e637883a04c..b7d86c03747 100644 --- a/instrgen/driver/main.go +++ b/instrgen/driver/main.go @@ -18,22 +18,16 @@ import ( "encoding/json" "errors" "fmt" + alib "go.opentelemetry.io/contrib/instrgen/lib" "go.opentelemetry.io/contrib/instrgen/rewriters" - "go/ast" - "go/build" "go/parser" "go/printer" "go/token" - "go/types" - "golang.org/x/tools/go/loader" "log" "os" "os/exec" "path/filepath" "strings" - "sync" - - alib "go.opentelemetry.io/contrib/instrgen/lib" ) func usage() error { @@ -41,8 +35,6 @@ func usage() error { fmt.Println("\tcommand:") fmt.Println("\t\tinject (injects open telemetry calls into project code)") fmt.Println("\t\tprune (prune open telemetry calls") - fmt.Println("\t\tdumpcfg (dumps control flow graph)") - fmt.Println("\t\trootfunctions (dumps root functions)") return nil } @@ -59,71 +51,6 @@ type InstrgenCmd struct { EntryPoint EntryPoint } -// Load whole go program. -func LoadProgram(projectPath string, ginfo *types.Info) (*loader.Program, error) { - cwd, err := os.Getwd() - if err != nil { - return nil, err - } - conf := loader.Config{ParserMode: parser.ParseComments} - conf.Build = &build.Default - conf.Build.CgoEnabled = false - conf.Build.Dir = filepath.Join(cwd, projectPath) - conf.Import(projectPath) - var mutex = &sync.RWMutex{} - conf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) { - for k, v := range info.Defs { - mutex.Lock() - ginfo.Defs[k] = v - mutex.Unlock() - } - for k, v := range info.Uses { - mutex.Lock() - ginfo.Uses[k] = v - mutex.Unlock() - } - for k, v := range info.Selections { - mutex.Lock() - ginfo.Selections[k] = v - mutex.Unlock() - } - } - return conf.Load() - -} - -func makeCallGraph(packagePattern string, prog *loader.Program, ginfo *types.Info) map[alib.FuncDescriptor][]alib.FuncDescriptor { - var backwardCallGraph map[alib.FuncDescriptor][]alib.FuncDescriptor - - interfaces := alib.GetInterfaces(ginfo.Defs) - funcsInfo := alib.FindFuncDecls(prog, ginfo, interfaces, packagePattern) - backwardCallGraph = alib.BuildCallGraph(prog, ginfo, funcsInfo, packagePattern) - - return backwardCallGraph -} - -func makeRootFunctions(prog *loader.Program, ginfo *types.Info, packagePattern string) []alib.FuncDescriptor { - var rootFunctions []alib.FuncDescriptor - interfaces := alib.GetInterfaces(ginfo.Defs) - rootFunctions = append(rootFunctions, alib.FindRootFunctions(prog, ginfo, interfaces, packagePattern)...) - return rootFunctions -} - -func dumpCallGraph(callGraph map[alib.FuncDescriptor][]alib.FuncDescriptor) { - fmt.Println("\n\tchild parent") - for k, v := range callGraph { - fmt.Print("\n\t", k) - fmt.Print(" ", v) - } -} - -func dumpRootFunctions(rootFunctions []alib.FuncDescriptor) { - fmt.Println("rootfunctions:") - for _, fun := range rootFunctions { - fmt.Println("\t" + fun.TypeHash()) - } -} - func isDirectory(path string) (bool, error) { fileInfo, err := os.Stat(path) if err != nil { @@ -142,11 +69,6 @@ func executeCommand(command string, projectPath string, packagePattern string, r if err != nil { return err } - ginfo := &types.Info{ - Defs: make(map[*ast.Ident]types.Object), - Uses: make(map[*ast.Ident]types.Object), - Selections: make(map[*ast.SelectorExpr]*types.Selection), - } switch command { case "--inject": @@ -165,25 +87,6 @@ func executeCommand(command string, projectPath string, packagePattern string, r log.Fatal(err) } return nil - case "--dumpcfg": - prog, err := LoadProgram(projectPath, ginfo) - if err != nil { - fmt.Println(err) - return err - } - - backwardCallGraph := makeCallGraph(packagePattern, prog, ginfo) - dumpCallGraph(backwardCallGraph) - return nil - case "--rootfunctions": - prog, err := LoadProgram(projectPath, ginfo) - if err != nil { - fmt.Println(err) - return err - } - rootFunctions := makeRootFunctions(prog, ginfo, packagePattern) - dumpRootFunctions(rootFunctions) - return nil case "--prune": entry := strings.Split(entryPoint, ".") data := InstrgenCmd{projectPath, packagePattern, "prune", "yes", diff --git a/instrgen/lib/callgraph.go b/instrgen/lib/callgraph.go deleted file mode 100644 index 039114d05d6..00000000000 --- a/instrgen/lib/callgraph.go +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package lib // import "go.opentelemetry.io/contrib/instrgen/lib" -import ( - "fmt" - "go/ast" - "go/types" - "strings" - - "golang.org/x/tools/go/loader" -) - -// FuncDescriptor stores an information about -// id, type and if function requires custom instrumentation. -type FuncDescriptor struct { - PackageName string - Receiver string - FunctionName string - FuncType string - FileName string - Line int -} - -// InterfaceImplMapping. Mapping interface to implementation. -type InterfaceImplMapping = map[string][]*types.Var - -// FuncsInfo stores an information about -// function declarations. -type FuncsInfo struct { - FuncDecls map[FuncDescriptor]bool - InterfaceImplMapping InterfaceImplMapping -} - -func makeFuncsInfo() FuncsInfo { - return FuncsInfo{ - FuncDecls: make(map[FuncDescriptor]bool), - InterfaceImplMapping: make(map[string][]*types.Var), - } -} - -// Id. FunctionId. -func (fd FuncDescriptor) Id() string { - recvStr := fd.Receiver - return fd.PackageName + recvStr + "." + fd.FunctionName + "." + fd.FuncType -} - -// TypeHash. Function hash. -func (fd FuncDescriptor) TypeHash() string { - recvStr := fd.Receiver - return fd.PackageName + recvStr + "." + fd.FunctionName + "." + fd.FuncType -} - -// GetInterfaces returns all interfaces. -func GetInterfaces(defs map[*ast.Ident]types.Object) map[string]types.Object { - interfaces := make(map[string]types.Object) - for id, obj := range defs { - if obj == nil || obj.Type() == nil { - continue - } - if _, ok := obj.(*types.TypeName); !ok { - continue - } - if types.IsInterface(obj.Type()) { - interfaces[id.Name] = obj - } - } - return interfaces -} - -func isAny(obj types.Object) bool { - return obj.Type().String() == "any" || obj.Type().Underlying().String() == "any" -} - -func getInterfaceNameForReceiver(interfaces map[string]types.Object, recv *types.Var) []types.Object { - var interfacesMap []types.Object - for _, obj := range interfaces { - if t, ok := obj.Type().Underlying().(*types.Interface); ok { - if types.Implements(recv.Type(), t) && !isAny(obj) { - interfacesMap = append(interfacesMap, obj) - } - } - } - return interfacesMap -} - -func findRootFunctions(prog *loader.Program, file *ast.File, ginfo *types.Info, interfaces map[string]types.Object, functionLabel string, rootFunctions *[]FuncDescriptor) { - var parentFunc FuncDescriptor - ast.Inspect(file, func(n ast.Node) bool { - switch node := n.(type) { - case *ast.FuncDecl: - position := prog.Fset.Position(ginfo.Defs[node.Name].Pos()) - ftype := ginfo.Defs[node.Name].Type() - signature := ftype.(*types.Signature) - receiver := signature.Recv() - var receiverStr string - if receiver != nil { - receiverStr = receiver.Type().String() - } - parentFunc = FuncDescriptor{ginfo.Defs[node.Name].Pkg().String(), - receiverStr, node.Name.String(), ftype.String(), - position.Filename, position.Line} - - case *ast.CallExpr: - selector, ok := node.Fun.(*ast.SelectorExpr) - if ok { - if selector.Sel.Name == functionLabel { - *rootFunctions = append(*rootFunctions, parentFunc) - } - } - } - return true - }) -} - -func findFuncDecls(prog *loader.Program, file *ast.File, - ginfo *types.Info, interfaces map[string]types.Object, - funcsInfo FuncsInfo) { - ast.Inspect(file, func(n ast.Node) bool { - if node, ok := n.(*ast.FuncDecl); ok { - ftype := ginfo.Defs[node.Name].Type() - signature := ftype.(*types.Signature) - receiver := signature.Recv() - - var interfaceObjs []types.Object - - var receiverStr string - if receiver != nil { - receiverStr = receiver.Type().String() - interfaceObjs = getInterfaceNameForReceiver(interfaces, receiver) - } - for _, interfaceObj := range interfaceObjs { - funcsInfo.InterfaceImplMapping[interfaceObj.Type().String()] = append(funcsInfo.InterfaceImplMapping[interfaceObj.Type().String()], receiver) - } - position := prog.Fset.Position(node.Name.Pos()) - funcDecl := FuncDescriptor{ginfo.Defs[node.Name].Pkg().String(), - receiverStr, node.Name.String(), ftype.String(), - position.Filename, position.Line} - funcsInfo.FuncDecls[funcDecl] = true - } - return true - }) -} - -// Dumps all funcdels. -func DumpFuncDecls(funcDecls map[FuncDescriptor]bool) { - fmt.Println("FuncDecls") - for fun := range funcDecls { - fmt.Println(fun) - } -} - -func addFuncCallToCallGraph(funcCall FuncDescriptor, currentFun FuncDescriptor, - funcDecls map[FuncDescriptor]bool, backwardCallGraph map[FuncDescriptor][]FuncDescriptor) { - if !Contains(backwardCallGraph[funcCall], currentFun) { - if _, ok := funcDecls[funcCall]; ok { - backwardCallGraph[funcCall] = append(backwardCallGraph[funcCall], currentFun) - } - } -} - -func buildCallGraph(prog *loader.Program, file *ast.File, ginfo *types.Info, - funcsInfo FuncsInfo, - backwardCallGraph map[FuncDescriptor][]FuncDescriptor) { - currentFun := FuncDescriptor{} - ast.Inspect(file, func(n ast.Node) bool { - switch node := n.(type) { - case *ast.FuncDecl: - ftype := ginfo.Defs[node.Name].Type() - signature := ftype.(*types.Signature) - recv := signature.Recv() - var recvStr string - if recv != nil { - recvStr = recv.Type().String() - } - position := prog.Fset.Position(ginfo.Defs[node.Name].Pos()) - currentFun = FuncDescriptor{ginfo.Defs[node.Name].Pkg().String(), - recvStr, node.Name.String(), ftype.String(), - position.Filename, position.Line} - case *ast.CallExpr: - switch node := node.Fun.(type) { - case *ast.Ident: - ftype := ginfo.Uses[node].Type() - pkg := "" - if ginfo.Uses[node].Pkg() != nil { - pkg = ginfo.Uses[node].Pkg().String() - } - position := prog.Fset.Position(ginfo.Uses[node].Pos()) - if ftype != nil { - funcCall := FuncDescriptor{pkg, "", node.Name, ftype.String(), - position.Filename, position.Line} - addFuncCallToCallGraph(funcCall, currentFun, funcsInfo.FuncDecls, backwardCallGraph) - } - case *ast.SelectorExpr: - obj := ginfo.Selections[node] - if obj != nil { - recv := obj.Recv() - // sel.Sel is function ident - ftype := ginfo.Uses[node.Sel] - var ftypeStr string - if ftype != nil { - ftypeStr = ftype.Type().String() - } - pkg := "" - if obj.Obj().Pkg() != nil { - pkg = obj.Obj().Pkg().String() - } - position := prog.Fset.Position(ginfo.Uses[node.Sel].Pos()) - funcCall := FuncDescriptor{pkg, recv.String(), obj.Obj().Name(), ftypeStr, - position.Filename, position.Line} - for _, impl := range funcsInfo.InterfaceImplMapping[recv.String()] { - implFuncCall := FuncDescriptor{impl.Pkg().String(), impl.Type().String(), - obj.Obj().Name(), ftypeStr, - position.Filename, position.Line} - addFuncCallToCallGraph(implFuncCall, currentFun, funcsInfo.FuncDecls, backwardCallGraph) - } - addFuncCallToCallGraph(funcCall, currentFun, funcsInfo.FuncDecls, backwardCallGraph) - } - } - } - return true - }) -} - -// Dumps fun calls. -func DumpFuncCalls(prog *loader.Program, file *ast.File, ginfo *types.Info) { - ast.Inspect(file, func(n ast.Node) bool { - if node, ok := n.(*ast.CallExpr); ok { - switch node := node.Fun.(type) { - case *ast.Ident: - position := prog.Fset.Position(ginfo.Uses[node].Pos()) - ftype := ginfo.Uses[node].Type() - if ftype != nil { - funcCall := FuncDescriptor{ginfo.Defs[node].Pkg().String(), "", node.Name, - ftype.String(), position.Filename, position.Line} - fmt.Println("FuncCall:", funcCall) - } - case *ast.SelectorExpr: - obj := ginfo.Selections[node] - if obj != nil { - recv := obj.Recv() - var ftypeStr string - // sel.Sel is function ident - ftype := ginfo.Uses[node.Sel] - - if ftype != nil { - ftypeStr = ftype.Type().String() - } - var recvStr string - if len(recv.String()) > 0 { - recvStr = "." + recv.String() - } - position := prog.Fset.Position(ginfo.Uses[node.Sel].Pos()) - funcCall := FuncDescriptor{obj.Obj().Pkg().String(), recvStr, - obj.Obj().Name(), ftypeStr, position.Filename, position.Line} - fmt.Println("FuncCall:", funcCall) - } - } - } - return true - }) -} - -// Dumps call graph. -func DumpCallGraph(backwardCallGraph map[FuncDescriptor][]FuncDescriptor) { - fmt.Println("\n\tchild parent") - for k, v := range backwardCallGraph { - fmt.Print("\n\t", k.Id()) - fmt.Print(" ", v) - } - fmt.Print("\n") -} - -// Finds entry points. -func FindRootFunctions(prog *loader.Program, ginfo *types.Info, interfaces map[string]types.Object, allowedPathPattern string) []FuncDescriptor { - var rootFunctions []FuncDescriptor - for _, pkg := range prog.AllPackages { - //fmt.Printf("Package path %q\n", pkg.Pkg.Path()) - for _, file := range pkg.Files { - if allowedPathPattern != "" && !strings.Contains(prog.Fset.Position(file.Name.Pos()).String(), allowedPathPattern) { - continue - } - //fmt.Println(prog.Fset.Position(file.Name.Pos()).String()) - findRootFunctions(prog, file, ginfo, interfaces, "AutotelEntryPoint", &rootFunctions) - } - } - return rootFunctions -} - -// Searches for fun decls. -func FindFuncDecls(prog *loader.Program, ginfo *types.Info, interfaces map[string]types.Object, allowedPathPattern string) FuncsInfo { - funcsInfo := makeFuncsInfo() - for _, pkg := range prog.AllPackages { - //fmt.Printf("Package path %q\n", pkg.Pkg.Path()) - for _, file := range pkg.Files { - if allowedPathPattern != "" && !strings.Contains(prog.Fset.Position(file.Name.Pos()).String(), allowedPathPattern) { - continue - } - //fmt.Println(prog.Fset.Position(file.Name.Pos()).String()) - findFuncDecls(prog, file, ginfo, interfaces, funcsInfo) - } - } - return funcsInfo -} - -// Builds call graph. -func BuildCallGraph(prog *loader.Program, ginfo *types.Info, - funcsInfo FuncsInfo, allowedPathPattern string) map[FuncDescriptor][]FuncDescriptor { - backwardCallGraph := make(map[FuncDescriptor][]FuncDescriptor) - for _, pkg := range prog.AllPackages { - //fmt.Printf("Package path %q\n", pkg.Pkg.Path()) - for _, file := range pkg.Files { - if allowedPathPattern != "" && !strings.Contains(prog.Fset.Position(file.Name.Pos()).String(), allowedPathPattern) { - continue - } - //fmt.Println(prog.Fset.Position(file.Name.Pos()).String()) - buildCallGraph(prog, file, ginfo, funcsInfo, backwardCallGraph) - } - } - return backwardCallGraph -} diff --git a/instrgen/lib/tools.go b/instrgen/lib/tools.go index 7f59d26e5da..0a461ae0488 100644 --- a/instrgen/lib/tools.go +++ b/instrgen/lib/tools.go @@ -34,16 +34,6 @@ func SearchFiles(root string, ext string) []string { return files } -// Contains. -func Contains(a []FuncDescriptor, x FuncDescriptor) bool { - for _, n := range a { - if x.Id() == n.Id() { - return true - } - } - return false -} - // FileExists. func FileExists(filename string) bool { info, err := os.Stat(filename)