Skip to content
This repository was archived by the owner on Aug 17, 2020. It is now read-only.

Commit 9d2f515

Browse files
authored
Improvements from Buildkit instrumentation (#214)
* Add an 'SetTestCodeFromCaller' API to set the test code * Get exact test start time * Bump agent pre version * Add an 'SetTestCodeFromCaller' API to set the test code * Get exact test start time * Bump agent pre version * Agent benchmark test * Logger patch benchmark * SetTestCodeFromCallerSkip and SetTestCodeFromFunc * Test instrumentation init benchmark * Benchmark dependencies and git tests * Benchmark monkey patching testing logger * Test and Benchmark Logger Patcher * Reflection test * ParallelByReflection test * remove data race * Remove reflection.Value arguments usage outside the dynamic func call * format fixes * test fix * Fix util tests
1 parent 1a9f75b commit 9d2f515

File tree

15 files changed

+303
-114
lines changed

15 files changed

+303
-114
lines changed

agent/agent.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ type (
6262
)
6363

6464
var (
65-
version = "0.1.16-pre1"
65+
version = "0.1.16-pre2"
6666

6767
testingModeFrequency = time.Second
6868
nonTestingModeFrequency = time.Minute

agent/agent_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os/exec"
66
"reflect"
77
"strings"
8+
"sync"
89
"testing"
910
"time"
1011

@@ -141,3 +142,27 @@ func TestTildeExpandRaceMetadata(t *testing.T) {
141142
<-time.After(5 * time.Second)
142143
agent.Stop()
143144
}
145+
146+
var a *Agent
147+
148+
func BenchmarkNewAgent(b *testing.B) {
149+
for i := 0; i < b.N; i++ {
150+
var err error
151+
a, err = NewAgent(WithTestingModeEnabled(),
152+
WithHandlePanicAsFail(),
153+
WithRetriesOnFail(3),
154+
WithSetGlobalTracer())
155+
if err != nil {
156+
b.Fatal(err)
157+
}
158+
span := a.Tracer().StartSpan("Test")
159+
span.SetTag("span.kind", "test")
160+
span.SetTag("test.name", "BenchNewAgent")
161+
span.SetTag("test.suite", "root")
162+
span.SetTag("test.status", tags.TestStatus_PASS)
163+
span.SetBaggageItem("trace.kind", "test")
164+
span.Finish()
165+
once = sync.Once{}
166+
a.Stop()
167+
}
168+
}

agent/dependencies_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package agent
2+
3+
import "testing"
4+
5+
var mp map[string]string
6+
7+
func BenchmarkGetDependencyMap(b *testing.B) {
8+
for i := 0; i < b.N; i++ {
9+
mp = getDependencyMap()
10+
}
11+
}

agent/git_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,18 @@ func TestMergeRegex(t *testing.T) {
108108
}
109109
}
110110
}
111+
112+
var data *GitData
113+
var diff *GitDiff
114+
115+
func BenchmarkGetGitData(b *testing.B) {
116+
for i := 0; i < b.N; i++ {
117+
data = getGitData()
118+
}
119+
}
120+
121+
func BenchmarkGetGitDiff(b *testing.B) {
122+
for i := 0; i < b.N; i++ {
123+
diff = getGitDiff()
124+
}
125+
}

go.mod

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@ require (
1515
github.com/stretchr/testify v1.5.1
1616
github.com/undefinedlabs/go-mpatch v0.0.0-20200326085307-1a86426f42a6
1717
github.com/vmihailenco/msgpack v4.0.4+incompatible
18-
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 // indirect
1918
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
2019
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
21-
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 // indirect
2220
google.golang.org/appengine v1.6.5 // indirect
2321
google.golang.org/grpc v1.27.1
2422
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
2523
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
26-
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc // indirect
2724
)

init.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"os/signal"
8+
"reflect"
89
"runtime"
910
"sync"
1011
"syscall"
@@ -75,6 +76,32 @@ func GetContextFromTest(t *testing.T) context.Context {
7576
return context.TODO()
7677
}
7778

79+
// Sets the test code from the caller of this func
80+
func SetTestCodeFromCaller(t *testing.T) {
81+
SetTestCodeFromCallerSkip(t, 1)
82+
}
83+
84+
// Sets the test code from the caller of this func
85+
func SetTestCodeFromCallerSkip(t *testing.T, skip int) {
86+
test := GetTest(t)
87+
if test == nil {
88+
return
89+
}
90+
pc, _, _, _ := runtime.Caller(skip + 1)
91+
test.SetTestCode(pc)
92+
}
93+
94+
// Sets the test code from a func
95+
func SetTestCodeFromFunc(t *testing.T, fn interface{}) {
96+
test := GetTest(t)
97+
if test == nil {
98+
return
99+
}
100+
value := reflect.ValueOf(fn)
101+
pc := value.Pointer()
102+
test.SetTestCode(pc)
103+
}
104+
78105
// Gets the *Benchmark from a *testing.B
79106
func GetBenchmark(b *testing.B) *scopetesting.Benchmark {
80107
return scopetesting.GetBenchmark(b)

instrumentation/logging/logger_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,20 @@ func checkMessage(msg string, records []opentracing.LogRecord) bool {
132132
}
133133
return false
134134
}
135+
136+
func BenchmarkPatchStandardLogger(b *testing.B) {
137+
for i := 0; i < b.N; i++ {
138+
PatchStandardLogger()
139+
UnpatchStandardLogger()
140+
}
141+
}
142+
143+
var lg *stdlog.Logger
144+
145+
func BenchmarkPatchLogger(b *testing.B) {
146+
for i := 0; i < b.N; i++ {
147+
lg = stdlog.New(os.Stdout, "", stdlog.Llongfile|stdlog.Lmicroseconds)
148+
PatchLogger(lg)
149+
UnpatchLogger(lg)
150+
}
151+
}

instrumentation/testing/benchmark.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,10 @@ func startBenchmark(b *testing.B, pc uintptr, benchFunc func(b *testing.B)) {
111111
fullTestName = strings.Join(nameSegments, "/")
112112
}
113113
packageName := reflection.GetBenchmarkSuiteName(b)
114+
pName, _, tCode := getPackageAndNameAndBoundaries(pc)
115+
114116
if packageName == "" {
115-
packageName = getPackageName(pc, fullTestName)
117+
packageName = pName
116118
}
117119

118120
oTags := opentracing.Tags{
@@ -124,9 +126,8 @@ func startBenchmark(b *testing.B, pc uintptr, benchFunc func(b *testing.B)) {
124126
"test.type": "benchmark",
125127
}
126128

127-
testCode := getTestCodeBoundaries(pc, fullTestName)
128-
if testCode != "" {
129-
oTags["test.code"] = testCode
129+
if tCode != "" {
130+
oTags["test.code"] = tCode
130131
}
131132

132133
var startOptions []opentracing.StartSpanOption

instrumentation/testing/logger.go

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -52,84 +52,82 @@ func UnpatchTestingLogger() {
5252
}
5353

5454
func patchError() {
55-
patch("Error", func(test *Test, argsValues []reflect.Value) {
55+
patch("Error", func(test *Test, args []interface{}) {
5656
test.t.Helper()
57-
args := getArgs(argsValues[0])
5857
test.Error(args...)
5958
})
6059
}
6160

6261
func patchErrorf() {
63-
patch("Errorf", func(test *Test, argsValues []reflect.Value) {
62+
patch("Errorf", func(test *Test, args []interface{}) {
6463
test.t.Helper()
65-
format := argsValues[0].String()
66-
args := getArgs(argsValues[1])
67-
test.Errorf(format, args...)
64+
format := args[0].(string)
65+
test.Errorf(format, args[1:]...)
6866
})
6967
}
7068

7169
func patchFatal() {
72-
patch("Fatal", func(test *Test, argsValues []reflect.Value) {
70+
patch("Fatal", func(test *Test, args []interface{}) {
7371
test.t.Helper()
74-
args := getArgs(argsValues[0])
7572
test.Fatal(args...)
7673
})
7774
}
7875

7976
func patchFatalf() {
80-
patch("Fatalf", func(test *Test, argsValues []reflect.Value) {
77+
patch("Fatalf", func(test *Test, args []interface{}) {
8178
test.t.Helper()
82-
format := argsValues[0].String()
83-
args := getArgs(argsValues[1])
84-
test.Fatalf(format, args...)
79+
format := args[0].(string)
80+
test.Fatalf(format, args[1:]...)
8581
})
8682
}
8783

8884
func patchLog() {
89-
patch("Log", func(test *Test, argsValues []reflect.Value) {
85+
patch("Log", func(test *Test, args []interface{}) {
9086
test.t.Helper()
91-
args := getArgs(argsValues[0])
9287
test.Log(args...)
9388
})
9489
}
9590

9691
func patchLogf() {
97-
patch("Logf", func(test *Test, argsValues []reflect.Value) {
92+
patch("Logf", func(test *Test, args []interface{}) {
9893
test.t.Helper()
99-
format := argsValues[0].String()
100-
args := getArgs(argsValues[1])
101-
test.Logf(format, args...)
94+
format := args[0].(string)
95+
test.Logf(format, args[1:]...)
10296
})
10397
}
10498

10599
func patchSkip() {
106-
patch("Skip", func(test *Test, argsValues []reflect.Value) {
100+
patch("Skip", func(test *Test, args []interface{}) {
107101
test.t.Helper()
108-
args := getArgs(argsValues[0])
109102
test.Skip(args...)
110103
})
111104
}
112105

113106
func patchSkipf() {
114-
patch("Skipf", func(test *Test, argsValues []reflect.Value) {
107+
patch("Skipf", func(test *Test, args []interface{}) {
115108
test.t.Helper()
116-
format := argsValues[0].String()
117-
args := getArgs(argsValues[1])
118-
test.Skipf(format, args...)
109+
format := args[0].(string)
110+
test.Skipf(format, args[1:]...)
119111
})
120112
}
121113

122-
func getArgs(in reflect.Value) []interface{} {
114+
func createArgs(in []reflect.Value) []interface{} {
123115
var args []interface{}
124-
if in.Kind() == reflect.Slice {
125-
for i := 0; i < in.Len(); i++ {
126-
args = append(args, in.Index(i).Interface())
116+
for _, item := range in {
117+
if item.Kind() == reflect.Slice {
118+
var itemArg []interface{}
119+
for i := 0; i < item.Len(); i++ {
120+
itemArg = append(itemArg, item.Index(i).Interface())
121+
}
122+
args = append(args, itemArg)
123+
} else {
124+
args = append(args, item.Interface())
127125
}
128126
}
129127
return args
130128
}
131129

132-
func patch(methodName string, methodBody func(test *Test, argsValues []reflect.Value)) {
130+
func patch(methodName string, methodBody func(test *Test, argsValues []interface{})) {
133131
patchesMutex.Lock()
134132
defer patchesMutex.Unlock()
135133
patchPointersMutex.Lock()
@@ -144,6 +142,7 @@ func patch(methodName string, methodBody func(test *Test, argsValues []reflect.V
144142
var methodPatch *mpatch.Patch
145143
var err error
146144
methodPatch, err = mpatch.PatchMethodWithMakeFunc(method, func(in []reflect.Value) []reflect.Value {
145+
argIn := createArgs(in[1:])
147146
t := (*testing.T)(unsafe.Pointer(in[0].Pointer()))
148147
if t == nil {
149148
instrumentation.Logger().Println("testing.T is nil")
@@ -161,7 +160,7 @@ func patch(methodName string, methodBody func(test *Test, argsValues []reflect.V
161160
instrumentation.Logger().Printf("test struct for %v doesn't exist\n", t.Name())
162161
return nil
163162
}
164-
methodBody(test, in[1:])
163+
methodBody(test, argIn)
165164
return nil
166165
})
167166
logOnError(err)

instrumentation/testing/testing.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ import (
1919
"go.undefinedlabs.com/scopeagent/reflection"
2020
"go.undefinedlabs.com/scopeagent/runner"
2121
"go.undefinedlabs.com/scopeagent/tags"
22+
"go.undefinedlabs.com/scopeagent/tracer"
2223
)
2324

2425
type (
2526
Test struct {
2627
testing.TB
27-
ctx context.Context
28-
span opentracing.Span
29-
t *testing.T
28+
ctx context.Context
29+
span opentracing.Span
30+
t *testing.T
31+
codePC uintptr
3032
}
3133

3234
Option func(*Test)
@@ -64,6 +66,7 @@ func StartTestFromCaller(t *testing.T, pc uintptr, opts ...Option) *Test {
6466
// If there is already one we want to replace it, so we clear the context
6567
test.ctx = context.Background()
6668
}
69+
test.codePC = pc
6770

6871
for _, opt := range opts {
6972
opt(test)
@@ -72,17 +75,16 @@ func StartTestFromCaller(t *testing.T, pc uintptr, opts ...Option) *Test {
7275
// Extracting the testing func name (by removing any possible sub-test suffix `{test_func}/{sub_test}`)
7376
// to search the func source code bounds and to calculate the package name.
7477
fullTestName := runner.GetOriginalTestName(t.Name())
75-
packageName := getPackageName(pc, fullTestName)
78+
pName, _, testCode := getPackageAndNameAndBoundaries(pc)
7679

7780
testTags := opentracing.Tags{
7881
"span.kind": "test",
7982
"test.name": fullTestName,
80-
"test.suite": packageName,
83+
"test.suite": pName,
8184
"test.framework": "testing",
8285
"test.language": "go",
8386
}
8487

85-
testCode := getTestCodeBoundaries(pc, fullTestName)
8688
if testCode != "" {
8789
testTags["test.code"] = testCode
8890
}
@@ -102,6 +104,15 @@ func StartTestFromCaller(t *testing.T, pc uintptr, opts ...Option) *Test {
102104
return test
103105
}
104106

107+
// Set test code
108+
func (test *Test) SetTestCode(pc uintptr) {
109+
pName, _, fBoundaries := getPackageAndNameAndBoundaries(pc)
110+
test.span.SetTag("test.suite", pName)
111+
if fBoundaries != "" {
112+
test.span.SetTag("test.code", fBoundaries)
113+
}
114+
}
115+
105116
// Ends the current test
106117
func (test *Test) End() {
107118
autoInstrumentedTestsMutex.RLock()
@@ -135,6 +146,15 @@ func (test *Test) Run(name string, f func(t *testing.T)) bool {
135146
func (test *Test) end() {
136147
finishTime := time.Now()
137148

149+
// If we have our own implementation of the span, we can set the exact start time from the test
150+
if ownSpan, ok := test.span.(tracer.Span); ok {
151+
if startTime, err := reflection.GetTestStartTime(test.t); err == nil {
152+
ownSpan.SetStart(startTime)
153+
} else {
154+
instrumentation.Logger().Printf("error: %v", err)
155+
}
156+
}
157+
138158
// Remove the Test struct from the hash map, so a call to Start while we end this instance will create a new struct
139159
removeTest(test.t)
140160
// Stop and get records generated by loggers

0 commit comments

Comments
 (0)