Source file
src/runtime/trace_cgo_test.go
1
2
3
4
5
6
7 package runtime_test
8
9 import (
10 "bytes"
11 "fmt"
12 "internal/testenv"
13 "internal/trace"
14 tracev2 "internal/trace/v2"
15 "io"
16 "os"
17 "runtime"
18 "strings"
19 "testing"
20 )
21
22
23
24
25 func TestTraceUnwindCGO(t *testing.T) {
26 if *flagQuick {
27 t.Skip("-quick")
28 }
29 testenv.MustHaveGoBuild(t)
30 t.Parallel()
31
32 exe, err := buildTestProg(t, "testprogcgo")
33 if err != nil {
34 t.Fatal(err)
35 }
36
37 wantLogs := []string{
38 "goCalledFromC",
39 "goCalledFromCThread",
40 }
41 logs := make(map[string]*trace.Event)
42 for _, category := range wantLogs {
43 logs[category] = nil
44 }
45 logsV2 := make(map[string]*tracev2.Event)
46 for _, category := range wantLogs {
47 logsV2[category] = nil
48 }
49 for _, tracefpunwindoff := range []int{1, 0} {
50 env := fmt.Sprintf("GODEBUG=tracefpunwindoff=%d", tracefpunwindoff)
51 got := runBuiltTestProg(t, exe, "Trace", env)
52 prefix, tracePath, found := strings.Cut(got, ":")
53 if !found || prefix != "trace path" {
54 t.Fatalf("unexpected output:\n%s\n", got)
55 }
56 defer os.Remove(tracePath)
57
58 traceData, err := os.ReadFile(tracePath)
59 if err != nil {
60 t.Fatalf("failed to read trace: %s", err)
61 }
62 for category := range logs {
63 event := mustFindLogV2(t, bytes.NewReader(traceData), category)
64 if wantEvent := logsV2[category]; wantEvent == nil {
65 logsV2[category] = &event
66 } else if got, want := dumpStackV2(&event), dumpStackV2(wantEvent); got != want {
67 t.Errorf("%q: got stack:\n%s\nwant stack:\n%s\n", category, got, want)
68 }
69 }
70 }
71 }
72
73
74
75 func mustFindLog(t *testing.T, events []*trace.Event, category string) *trace.Event {
76 t.Helper()
77 var candidates []*trace.Event
78 for _, e := range events {
79 if e.Type == trace.EvUserLog && len(e.SArgs) >= 1 && e.SArgs[0] == category {
80 candidates = append(candidates, e)
81 }
82 }
83 if len(candidates) == 0 {
84 t.Errorf("could not find log with category: %q", category)
85 } else if len(candidates) > 1 {
86 t.Errorf("found more than one log with category: %q", category)
87 }
88 return candidates[0]
89 }
90
91
92 func dumpStack(e *trace.Event) string {
93 var buf bytes.Buffer
94 for _, f := range e.Stk {
95 file := strings.TrimPrefix(f.File, runtime.GOROOT())
96 fmt.Fprintf(&buf, "%s\n\t%s:%d\n", f.Fn, file, f.Line)
97 }
98 return buf.String()
99 }
100
101
102
103 func parseTrace(t *testing.T, r io.Reader) []*trace.Event {
104 res, err := trace.Parse(r, "")
105 if err == trace.ErrTimeOrder {
106 t.Skipf("skipping trace: %v", err)
107 }
108 if err != nil {
109 t.Fatalf("failed to parse trace: %v", err)
110 }
111 return res.Events
112 }
113
114 func mustFindLogV2(t *testing.T, trace io.Reader, category string) tracev2.Event {
115 r, err := tracev2.NewReader(trace)
116 if err != nil {
117 t.Fatalf("bad trace: %v", err)
118 }
119 var candidates []tracev2.Event
120 for {
121 ev, err := r.ReadEvent()
122 if err == io.EOF {
123 break
124 }
125 if err != nil {
126 t.Fatalf("failed to parse trace: %v", err)
127 }
128 if ev.Kind() == tracev2.EventLog && ev.Log().Category == category {
129 candidates = append(candidates, ev)
130 }
131 }
132 if len(candidates) == 0 {
133 t.Fatalf("could not find log with category: %q", category)
134 } else if len(candidates) > 1 {
135 t.Fatalf("found more than one log with category: %q", category)
136 }
137 return candidates[0]
138 }
139
140
141 func dumpStackV2(e *tracev2.Event) string {
142 var buf bytes.Buffer
143 e.Stack().Frames(func(f tracev2.StackFrame) bool {
144 file := strings.TrimPrefix(f.File, runtime.GOROOT())
145 fmt.Fprintf(&buf, "%s\n\t%s:%d\n", f.Func, file, f.Line)
146 return true
147 })
148 return buf.String()
149 }
150
View as plain text