Skip to content

Commit

Permalink
Add instrgen implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
pdelewski committed Dec 22, 2022
1 parent 54f0bc5 commit ea147d9
Show file tree
Hide file tree
Showing 35 changed files with 3,627 additions and 0 deletions.
18 changes: 18 additions & 0 deletions .github/dependabot.yml
Expand Up @@ -64,6 +64,24 @@ updates:
schedule:
interval: weekly
day: sunday
- package-ecosystem: gomod
directory: /instrgen
labels:
- dependencies
- go
- Skip Changelog
schedule:
interval: weekly
day: sunday
- package-ecosystem: gomod
directory: /instrgen/driver
labels:
- dependencies
- go
- Skip Changelog
schedule:
interval: weekly
day: sunday
- package-ecosystem: gomod
directory: /instrumentation/github.com/Shopify/sarama/otelsarama
labels:
Expand Down
99 changes: 99 additions & 0 deletions instrgen/driver/driver_test.go
@@ -0,0 +1,99 @@
// 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 main

import (
"bytes"
"fmt"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

alib "go.opentelemetry.io/contrib/instrgen/lib"
)

var testcases = map[string]string{
"./test/fib": "./test/expected/fib",
"./test/methods": "./test/expected/methods",
"./test/goroutines": "./test/expected/goroutines",
"./test/recursion": "./test/expected/recursion",
"./test/package": "./test/expected/package",
"./test/selector": "./test/expected/selector",
}

var failures []string

func inject(t *testing.T, root string, packagePattern string) {
var rootFunctions []alib.FuncDescriptor

rootFunctions = append(rootFunctions, alib.FindRootFunctions(root, packagePattern)...)
interfaces := alib.FindInterfaces(root, packagePattern)
funcDecls := alib.FindFuncDecls(root, packagePattern, interfaces)
backwardCallGraph := alib.BuildCallGraph(root, packagePattern, funcDecls, interfaces)

fmt.Println("\n\tchild parent")
for k, v := range backwardCallGraph {
fmt.Print("\n\t", k)
fmt.Print(" ", v)
}
fmt.Println("")

analysis := &alib.Analysis{ProjectPath: root,
PackagePattern: packagePattern,
RootFunctions: rootFunctions,
FuncDecls: funcDecls,
Callgraph: backwardCallGraph,
Interfaces: interfaces,
Debug: false}
err := ExecutePasses(analysis)
require.NoError(t, err)
}

func Test(t *testing.T) {
for k, v := range testcases {
inject(t, k, "./...")
files := alib.SearchFiles(k, ".go.input")
expectedFiles := alib.SearchFiles(v, ".go.expected")
numOfFiles := len(expectedFiles)
numOfComparisons := 0
for _, file := range files {
for _, expectedFile := range expectedFiles {
fmt.Println(file)
fmt.Println(expectedFile)
if filepath.Base(file) == filepath.Base(expectedFile) {
f1, err1 := os.ReadFile(file)
require.NoError(t, err1)
f2, err2 := os.ReadFile(expectedFile)
require.NoError(t, err2)
if !assert.True(t, bytes.Equal(f1, f2)) {
fmt.Println(k)
failures = append(failures, k)
}
numOfComparisons = numOfComparisons + 1
}
}
}
if numOfFiles != numOfComparisons {
panic("not all files were compared")
}
Prune(k, "./...")
}
for _, f := range failures {
fmt.Println("FAILURE : ", f)
}
}
25 changes: 25 additions & 0 deletions instrgen/driver/go.mod
@@ -0,0 +1,25 @@
module go.opentelemetry.io/contrib/instrgen/driver

go 1.18

replace go.opentelemetry.io/contrib/instrgen => ../

require (
github.com/stretchr/testify v1.8.1
go.opentelemetry.io/contrib/instrgen v0.0.0-00010101000000-000000000000
go.opentelemetry.io/otel v1.11.2
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.11.2 // indirect
go.opentelemetry.io/otel/sdk v1.11.2 // indirect
go.opentelemetry.io/otel/trace v1.11.2 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/tools v0.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
38 changes: 38 additions & 0 deletions instrgen/driver/go.sum
@@ -0,0 +1,38 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0=
go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.11.2 h1:BhEVgvuE1NWLLuMLvC6sif791F45KFHi5GhOs1KunZU=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.11.2/go.mod h1:bx//lU66dPzNT+Y0hHA12ciKoMOH9iixEwCqC1OeQWQ=
go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU=
go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU=
go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0=
go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
183 changes: 183 additions & 0 deletions instrgen/driver/main.go
@@ -0,0 +1,183 @@
// 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 main

import (
"fmt"
"log"
"os"

alib "go.opentelemetry.io/contrib/instrgen/lib"
)

func usage() {
fmt.Println("\nusage autotel --command [path to go project] [package pattern]")
fmt.Println("\tcommand:")
fmt.Println("\t\tinject (injects open telemetry calls into project code)")
fmt.Println("\t\tinject-dump-ir (injects open telemetry calls into project code and intermediate passes)")
fmt.Println("\t\tprune (prune open telemetry calls")
fmt.Println("\t\tdumpcfg (dumps control flow graph)")
fmt.Println("\t\tgencfg (generates json representation of control flow graph)")
fmt.Println("\t\trootfunctions (dumps root functions)")
}

// Prune.
func Prune(projectPath string, packagePattern string) {
var rootFunctions []alib.FuncDescriptor

interfaces := alib.FindInterfaces(projectPath, packagePattern)
rootFunctions = append(rootFunctions, alib.FindRootFunctions(projectPath, packagePattern)...)
funcDecls := alib.FindFuncDecls(projectPath, packagePattern, interfaces)
backwardCallGraph := alib.BuildCallGraph(projectPath, packagePattern, funcDecls, interfaces)
fmt.Println("\n\tchild parent")
for k, v := range backwardCallGraph {
fmt.Print("\n\t", k)
fmt.Print(" ", v)
}
fmt.Println("")
analysis := &alib.Analysis{
ProjectPath: projectPath,
PackagePattern: packagePattern,
RootFunctions: rootFunctions,
FuncDecls: funcDecls,
Callgraph: backwardCallGraph,
Interfaces: interfaces,
Debug: false}
err := analysis.Execute(&alib.OtelPruner{}, otelPrunerPassSuffix)
if err != nil {
log.Println("Pruning failed")
} else {
fmt.Println("\tpruning done")
}
}

// Parsing algorithm works as follows. It goes through all function
// decls and infer function bodies to find call to AutotelEntryPoint
// A parent function of this call will become root of instrumentation
// Each function call from this place will be instrumented automatically

func executeCommand(arglist []string) {
if arglist[1] == "--inject" {
projectPath := arglist[2]
packagePattern := arglist[3]
Prune(projectPath, packagePattern)
var rootFunctions []alib.FuncDescriptor

interfaces := alib.FindInterfaces(projectPath, packagePattern)
rootFunctions = append(rootFunctions, alib.FindRootFunctions(projectPath, packagePattern)...)
funcDecls := alib.FindFuncDecls(projectPath, packagePattern, interfaces)
backwardCallGraph := alib.BuildCallGraph(projectPath, packagePattern, funcDecls, interfaces)
fmt.Println("\n\tchild parent")
for k, v := range backwardCallGraph {
fmt.Print("\n\t", k)
fmt.Print(" ", v)
}
fmt.Println("")
analysis := &alib.Analysis{
ProjectPath: projectPath,
PackagePattern: packagePattern,
RootFunctions: rootFunctions,
FuncDecls: funcDecls,
Callgraph: backwardCallGraph,
Interfaces: interfaces,
Debug: false}
err := ExecutePasses(analysis)
if err != nil {
log.Println("Analysis failed")
} else {
fmt.Println("\tinstrumentation done")
}
}
if arglist[1] == "--inject-dump-ir" {
projectPath := arglist[2]
packagePattern := arglist[3]
Prune(projectPath, packagePattern)
var rootFunctions []alib.FuncDescriptor

interfaces := alib.FindInterfaces(projectPath, packagePattern)
rootFunctions = append(rootFunctions, alib.FindRootFunctions(projectPath, packagePattern)...)
funcDecls := alib.FindFuncDecls(projectPath, packagePattern, interfaces)
backwardCallGraph := alib.BuildCallGraph(projectPath, packagePattern, funcDecls, interfaces)
fmt.Println("\n\tchild parent")
for k, v := range backwardCallGraph {
fmt.Print("\n\t", k)
fmt.Print(" ", v)
}
fmt.Println("")
analysis := &alib.Analysis{
ProjectPath: projectPath,
PackagePattern: packagePattern,
RootFunctions: rootFunctions,
FuncDecls: funcDecls,
Callgraph: backwardCallGraph,
Interfaces: interfaces,
Debug: true}
err := ExecutePassesDumpIr(analysis)
if err != nil {
log.Println("Analysis failed")
} else {
fmt.Println("\tinstrumentation done")
}
}
if arglist[1] == "--dumpcfg" {
projectPath := arglist[2]
packagePattern := arglist[3]
var funcDecls map[alib.FuncDescriptor]bool
var backwardCallGraph map[alib.FuncDescriptor][]alib.FuncDescriptor

interfaces := alib.FindInterfaces(projectPath, packagePattern)
funcDecls = alib.FindFuncDecls(projectPath, packagePattern, interfaces)
backwardCallGraph = alib.BuildCallGraph(projectPath, packagePattern, funcDecls, interfaces)
fmt.Println("\n\tchild parent")
for k, v := range backwardCallGraph {
fmt.Print("\n\t", k)
fmt.Print(" ", v)
}
}
if arglist[1] == "--gencfg" {
projectPath := arglist[2]
packagePattern := arglist[3]
var funcDecls map[alib.FuncDescriptor]bool
var backwardCallGraph map[alib.FuncDescriptor][]alib.FuncDescriptor
interfaces := alib.FindInterfaces(projectPath, packagePattern)
funcDecls = alib.FindFuncDecls(projectPath, packagePattern, interfaces)
backwardCallGraph = alib.BuildCallGraph(projectPath, packagePattern, funcDecls, interfaces)
alib.Generatecfg(backwardCallGraph, "callgraph.js")
}
if arglist[1] == "--rootfunctions" {
projectPath := arglist[2]
packagePattern := arglist[3]
var rootFunctions []alib.FuncDescriptor
rootFunctions = append(rootFunctions, alib.FindRootFunctions(projectPath, packagePattern)...)
fmt.Println("rootfunctions:")
for _, fun := range rootFunctions {
fmt.Println("\t" + fun.TypeHash())
}
}
if arglist[1] == "--prune" {
projectPath := arglist[2]
packagePattern := arglist[3]
Prune(projectPath, packagePattern)
}
}

func main() {
fmt.Println("autotel compiler")
if len(os.Args) != 4 {
usage()
os.Exit(-1)
}
executeCommand(os.Args)
}

0 comments on commit ea147d9

Please sign in to comment.