Source file
src/runtime/trace/trace_stack_test.go
1
2
3
4
5 package trace_test
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/goexperiment"
11 "internal/testenv"
12 "internal/trace"
13 "net"
14 "os"
15 "runtime"
16 . "runtime/trace"
17 "strings"
18 "sync"
19 "testing"
20 "text/tabwriter"
21 "time"
22 )
23
24
25
26
27 func TestTraceSymbolize(t *testing.T) {
28 skipTraceSymbolizeTestIfNecessary(t)
29
30 buf := new(bytes.Buffer)
31 if err := Start(buf); err != nil {
32 t.Fatalf("failed to start tracing: %v", err)
33 }
34 defer Stop()
35
36
37
38
39
40
41 go func() {
42 select {}
43 }()
44 go func() {
45 var c chan int
46 c <- 0
47 }()
48 go func() {
49 var c chan int
50 <-c
51 }()
52 done1 := make(chan bool)
53 go func() {
54 <-done1
55 }()
56 done2 := make(chan bool)
57 go func() {
58 done2 <- true
59 }()
60 c1 := make(chan int)
61 c2 := make(chan int)
62 go func() {
63 select {
64 case <-c1:
65 case <-c2:
66 }
67 }()
68 var mu sync.Mutex
69 mu.Lock()
70 go func() {
71 mu.Lock()
72 mu.Unlock()
73 }()
74 var wg sync.WaitGroup
75 wg.Add(1)
76 go func() {
77 wg.Wait()
78 }()
79 cv := sync.NewCond(&sync.Mutex{})
80 go func() {
81 cv.L.Lock()
82 cv.Wait()
83 cv.L.Unlock()
84 }()
85 ln, err := net.Listen("tcp", "127.0.0.1:0")
86 if err != nil {
87 t.Fatalf("failed to listen: %v", err)
88 }
89 go func() {
90 c, err := ln.Accept()
91 if err != nil {
92 t.Errorf("failed to accept: %v", err)
93 return
94 }
95 c.Close()
96 }()
97 rp, wp, err := os.Pipe()
98 if err != nil {
99 t.Fatalf("failed to create a pipe: %v", err)
100 }
101 defer rp.Close()
102 defer wp.Close()
103 pipeReadDone := make(chan bool)
104 go func() {
105 var data [1]byte
106 rp.Read(data[:])
107 pipeReadDone <- true
108 }()
109
110 time.Sleep(100 * time.Millisecond)
111 runtime.GC()
112 runtime.Gosched()
113 time.Sleep(100 * time.Millisecond)
114 done1 <- true
115 <-done2
116 select {
117 case c1 <- 0:
118 case c2 <- 0:
119 }
120 mu.Unlock()
121 wg.Done()
122 cv.Signal()
123 c, err := net.Dial("tcp", ln.Addr().String())
124 if err != nil {
125 t.Fatalf("failed to dial: %v", err)
126 }
127 c.Close()
128 var data [1]byte
129 wp.Write(data[:])
130 <-pipeReadDone
131
132 oldGoMaxProcs := runtime.GOMAXPROCS(0)
133 runtime.GOMAXPROCS(oldGoMaxProcs + 1)
134
135 Stop()
136
137 runtime.GOMAXPROCS(oldGoMaxProcs)
138
139 events, _ := parseTrace(t, buf)
140
141
142 type eventDesc struct {
143 Type byte
144 Stk []frame
145 }
146 want := []eventDesc{
147 {trace.EvGCStart, []frame{
148 {"runtime.GC", 0},
149 {"runtime/trace_test.TestTraceSymbolize", 0},
150 {"testing.tRunner", 0},
151 }},
152 {trace.EvGoStart, []frame{
153 {"runtime/trace_test.TestTraceSymbolize.func1", 0},
154 }},
155 {trace.EvGoSched, []frame{
156 {"runtime/trace_test.TestTraceSymbolize", 112},
157 {"testing.tRunner", 0},
158 }},
159 {trace.EvGoCreate, []frame{
160 {"runtime/trace_test.TestTraceSymbolize", 41},
161 {"testing.tRunner", 0},
162 }},
163 {trace.EvGoStop, []frame{
164 {"runtime.block", 0},
165 {"runtime/trace_test.TestTraceSymbolize.func1", 0},
166 }},
167 {trace.EvGoStop, []frame{
168 {"runtime.chansend1", 0},
169 {"runtime/trace_test.TestTraceSymbolize.func2", 0},
170 }},
171 {trace.EvGoStop, []frame{
172 {"runtime.chanrecv1", 0},
173 {"runtime/trace_test.TestTraceSymbolize.func3", 0},
174 }},
175 {trace.EvGoBlockRecv, []frame{
176 {"runtime.chanrecv1", 0},
177 {"runtime/trace_test.TestTraceSymbolize.func4", 0},
178 }},
179 {trace.EvGoUnblock, []frame{
180 {"runtime.chansend1", 0},
181 {"runtime/trace_test.TestTraceSymbolize", 114},
182 {"testing.tRunner", 0},
183 }},
184 {trace.EvGoBlockSend, []frame{
185 {"runtime.chansend1", 0},
186 {"runtime/trace_test.TestTraceSymbolize.func5", 0},
187 }},
188 {trace.EvGoUnblock, []frame{
189 {"runtime.chanrecv1", 0},
190 {"runtime/trace_test.TestTraceSymbolize", 115},
191 {"testing.tRunner", 0},
192 }},
193 {trace.EvGoBlockSelect, []frame{
194 {"runtime.selectgo", 0},
195 {"runtime/trace_test.TestTraceSymbolize.func6", 0},
196 }},
197 {trace.EvGoUnblock, []frame{
198 {"runtime.selectgo", 0},
199 {"runtime/trace_test.TestTraceSymbolize", 116},
200 {"testing.tRunner", 0},
201 }},
202 {trace.EvGoBlockSync, []frame{
203 {"sync.(*Mutex).Lock", 0},
204 {"runtime/trace_test.TestTraceSymbolize.func7", 0},
205 }},
206 {trace.EvGoUnblock, []frame{
207 {"sync.(*Mutex).Unlock", 0},
208 {"runtime/trace_test.TestTraceSymbolize", 0},
209 {"testing.tRunner", 0},
210 }},
211 {trace.EvGoBlockSync, []frame{
212 {"sync.(*WaitGroup).Wait", 0},
213 {"runtime/trace_test.TestTraceSymbolize.func8", 0},
214 }},
215 {trace.EvGoUnblock, []frame{
216 {"sync.(*WaitGroup).Add", 0},
217 {"sync.(*WaitGroup).Done", 0},
218 {"runtime/trace_test.TestTraceSymbolize", 121},
219 {"testing.tRunner", 0},
220 }},
221 {trace.EvGoBlockCond, []frame{
222 {"sync.(*Cond).Wait", 0},
223 {"runtime/trace_test.TestTraceSymbolize.func9", 0},
224 }},
225 {trace.EvGoUnblock, []frame{
226 {"sync.(*Cond).Signal", 0},
227 {"runtime/trace_test.TestTraceSymbolize", 0},
228 {"testing.tRunner", 0},
229 }},
230 {trace.EvGoSleep, []frame{
231 {"time.Sleep", 0},
232 {"runtime/trace_test.TestTraceSymbolize", 0},
233 {"testing.tRunner", 0},
234 }},
235 {trace.EvGomaxprocs, []frame{
236 {"runtime.startTheWorld", 0},
237 {"runtime.startTheWorldGC", 0},
238 {"runtime.GOMAXPROCS", 0},
239 {"runtime/trace_test.TestTraceSymbolize", 0},
240 {"testing.tRunner", 0},
241 }},
242 }
243
244 if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
245 want = append(want, []eventDesc{
246 {trace.EvGoBlockNet, []frame{
247 {"internal/poll.(*FD).Accept", 0},
248 {"net.(*netFD).accept", 0},
249 {"net.(*TCPListener).accept", 0},
250 {"net.(*TCPListener).Accept", 0},
251 {"runtime/trace_test.TestTraceSymbolize.func10", 0},
252 }},
253 {trace.EvGoSysCall, []frame{
254 {"syscall.read", 0},
255 {"syscall.Read", 0},
256 {"internal/poll.ignoringEINTRIO", 0},
257 {"internal/poll.(*FD).Read", 0},
258 {"os.(*File).read", 0},
259 {"os.(*File).Read", 0},
260 {"runtime/trace_test.TestTraceSymbolize.func11", 0},
261 }},
262 }...)
263 }
264 matched := make([]bool, len(want))
265 for _, ev := range events {
266 wantLoop:
267 for i, w := range want {
268 if matched[i] || w.Type != ev.Type || len(w.Stk) != len(ev.Stk) {
269 continue
270 }
271
272 for fi, f := range ev.Stk {
273 wf := w.Stk[fi]
274 if wf.Fn != f.Fn || wf.Line != 0 && wf.Line != f.Line {
275 continue wantLoop
276 }
277 }
278 matched[i] = true
279 }
280 }
281 for i, w := range want {
282 if matched[i] {
283 continue
284 }
285 seen, n := dumpEventStacks(w.Type, events)
286 t.Errorf("Did not match event %v with stack\n%s\nSeen %d events of the type\n%s",
287 trace.EventDescriptions[w.Type].Name, dumpFrames(w.Stk), n, seen)
288 }
289 }
290
291 func skipTraceSymbolizeTestIfNecessary(t *testing.T) {
292 testenv.MustHaveGoBuild(t)
293 if goexperiment.ExecTracer2 {
294
295 t.Skip("skipping because this test is incompatible with the new tracer")
296 }
297 if IsEnabled() {
298 t.Skip("skipping because -test.trace is set")
299 }
300 }
301
302 func dumpEventStacks(typ byte, events []*trace.Event) ([]byte, int) {
303 matched := 0
304 o := new(bytes.Buffer)
305 tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
306 for _, ev := range events {
307 if ev.Type != typ {
308 continue
309 }
310 matched++
311 fmt.Fprintf(tw, "Offset %d\n", ev.Off)
312 for _, f := range ev.Stk {
313 fname := f.File
314 if idx := strings.Index(fname, "/go/src/"); idx > 0 {
315 fname = fname[idx:]
316 }
317 fmt.Fprintf(tw, " %v\t%s:%d\n", f.Fn, fname, f.Line)
318 }
319 }
320 tw.Flush()
321 return o.Bytes(), matched
322 }
323
324 type frame struct {
325 Fn string
326 Line int
327 }
328
329 func dumpFrames(frames []frame) []byte {
330 o := new(bytes.Buffer)
331 tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
332
333 for _, f := range frames {
334 fmt.Fprintf(tw, " %v\t :%d\n", f.Fn, f.Line)
335 }
336 tw.Flush()
337 return o.Bytes()
338 }
339
View as plain text