1
2
3
4
5 package trace_test
6
7 import (
8 "bytes"
9 "flag"
10 "internal/race"
11 "internal/trace"
12 "io"
13 "net"
14 "os"
15 "runtime"
16 . "runtime/trace"
17 "strconv"
18 "sync"
19 "testing"
20 "time"
21 )
22
23 var (
24 saveTraces = flag.Bool("savetraces", false, "save traces collected by tests")
25 )
26
27
28
29 func TestEventBatch(t *testing.T) {
30 if race.Enabled {
31 t.Skip("skipping in race mode")
32 }
33 if IsEnabled() {
34 t.Skip("skipping because -test.trace is set")
35 }
36 if testing.Short() {
37 t.Skip("skipping in short mode")
38 }
39
40
41
42
43
44
45
46
47
48
49 for g := 4950; g < 5050; g++ {
50 n := g
51 t.Run("G="+strconv.Itoa(n), func(t *testing.T) {
52 var wg sync.WaitGroup
53 wg.Add(n)
54
55 in := make(chan bool, 1000)
56 for i := 0; i < n; i++ {
57 go func() {
58 <-in
59 wg.Done()
60 }()
61 }
62 buf := new(bytes.Buffer)
63 if err := Start(buf); err != nil {
64 t.Fatalf("failed to start tracing: %v", err)
65 }
66
67 for i := 0; i < n; i++ {
68 in <- true
69 }
70 wg.Wait()
71 Stop()
72
73 _, err := trace.Parse(buf, "")
74 if err == trace.ErrTimeOrder {
75 t.Skipf("skipping trace: %v", err)
76 }
77
78 if err != nil {
79 t.Fatalf("failed to parse trace: %v", err)
80 }
81 })
82 }
83 }
84
85 func TestTraceStartStop(t *testing.T) {
86 if IsEnabled() {
87 t.Skip("skipping because -test.trace is set")
88 }
89 buf := new(bytes.Buffer)
90 if err := Start(buf); err != nil {
91 t.Fatalf("failed to start tracing: %v", err)
92 }
93 Stop()
94 size := buf.Len()
95 if size == 0 {
96 t.Fatalf("trace is empty")
97 }
98 time.Sleep(100 * time.Millisecond)
99 if size != buf.Len() {
100 t.Fatalf("trace writes after stop: %v -> %v", size, buf.Len())
101 }
102 saveTrace(t, buf, "TestTraceStartStop")
103 }
104
105 func TestTraceDoubleStart(t *testing.T) {
106 if IsEnabled() {
107 t.Skip("skipping because -test.trace is set")
108 }
109 Stop()
110 buf := new(bytes.Buffer)
111 if err := Start(buf); err != nil {
112 t.Fatalf("failed to start tracing: %v", err)
113 }
114 if err := Start(buf); err == nil {
115 t.Fatalf("succeed to start tracing second time")
116 }
117 Stop()
118 Stop()
119 }
120
121 func TestTrace(t *testing.T) {
122 if IsEnabled() {
123 t.Skip("skipping because -test.trace is set")
124 }
125 buf := new(bytes.Buffer)
126 if err := Start(buf); err != nil {
127 t.Fatalf("failed to start tracing: %v", err)
128 }
129 Stop()
130 saveTrace(t, buf, "TestTrace")
131 _, err := trace.Parse(buf, "")
132 if err == trace.ErrTimeOrder {
133 t.Skipf("skipping trace: %v", err)
134 }
135 if err != nil {
136 t.Fatalf("failed to parse trace: %v", err)
137 }
138 }
139
140 func parseTrace(t *testing.T, r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc) {
141 res, err := trace.Parse(r, "")
142 if err == trace.ErrTimeOrder {
143 t.Skipf("skipping trace: %v", err)
144 }
145 if err != nil {
146 t.Fatalf("failed to parse trace: %v", err)
147 }
148 gs := trace.GoroutineStats(res.Events)
149 for goid := range gs {
150
151
152 _ = trace.RelatedGoroutines(res.Events, goid)
153 }
154 return res.Events, gs
155 }
156
157 func testBrokenTimestamps(t *testing.T, data []byte) {
158
159
160
161
162
163
164
165
166 trace.BreakTimestampsForTesting = true
167 defer func() {
168 trace.BreakTimestampsForTesting = false
169 }()
170 for i := 0; i < 1e4; i++ {
171 _, err := trace.Parse(bytes.NewReader(data), "")
172 if err == trace.ErrTimeOrder {
173 return
174 }
175 if err != nil {
176 t.Fatalf("failed to parse trace: %v", err)
177 }
178 }
179 }
180
181 func TestTraceStress(t *testing.T) {
182 if runtime.GOOS == "js" {
183 t.Skip("no os.Pipe on js")
184 }
185 if IsEnabled() {
186 t.Skip("skipping because -test.trace is set")
187 }
188 if testing.Short() {
189 t.Skip("skipping in -short mode")
190 }
191
192 var wg sync.WaitGroup
193 done := make(chan bool)
194
195
196 wg.Add(1)
197 go func() {
198 <-done
199 wg.Done()
200 }()
201
202
203 rp, wp, err := os.Pipe()
204 if err != nil {
205 t.Fatalf("failed to create pipe: %v", err)
206 }
207 defer func() {
208 rp.Close()
209 wp.Close()
210 }()
211 wg.Add(1)
212 go func() {
213 var tmp [1]byte
214 rp.Read(tmp[:])
215 <-done
216 wg.Done()
217 }()
218 time.Sleep(time.Millisecond)
219
220 buf := new(bytes.Buffer)
221 if err := Start(buf); err != nil {
222 t.Fatalf("failed to start tracing: %v", err)
223 }
224
225 procs := runtime.GOMAXPROCS(10)
226 time.Sleep(50 * time.Millisecond)
227
228 go func() {
229 runtime.LockOSThread()
230 for {
231 select {
232 case <-done:
233 return
234 default:
235 runtime.Gosched()
236 }
237 }
238 }()
239
240 runtime.GC()
241
242 n := int(1e3)
243 if isMemoryConstrained() {
244
245
246 n = 512
247 }
248 for i := 0; i < n; i++ {
249 _ = make([]byte, 1<<20)
250 }
251
252
253 for p := 0; p < 10; p++ {
254 wg.Add(1)
255 go func() {
256
257 tmp := make([]byte, 1<<16)
258 for i := range tmp {
259 tmp[i]++
260 }
261 _ = tmp
262 <-done
263 wg.Done()
264 }()
265 }
266
267
268 wg.Add(1)
269 go func() {
270 var tmp [1]byte
271 rp.Read(tmp[:])
272 <-done
273 wg.Done()
274 }()
275
276
277 timerDone := make(chan bool)
278 go func() {
279 time.Sleep(time.Millisecond)
280 timerDone <- true
281 }()
282 <-timerDone
283
284
285 ln, err := net.Listen("tcp", "127.0.0.1:0")
286 if err != nil {
287 t.Fatalf("listen failed: %v", err)
288 }
289 defer ln.Close()
290 go func() {
291 c, err := ln.Accept()
292 if err != nil {
293 return
294 }
295 time.Sleep(time.Millisecond)
296 var buf [1]byte
297 c.Write(buf[:])
298 c.Close()
299 }()
300 c, err := net.Dial("tcp", ln.Addr().String())
301 if err != nil {
302 t.Fatalf("dial failed: %v", err)
303 }
304 var tmp [1]byte
305 c.Read(tmp[:])
306 c.Close()
307
308 go func() {
309 runtime.Gosched()
310 select {}
311 }()
312
313
314 wp.Write(tmp[:])
315 wp.Write(tmp[:])
316 close(done)
317 wg.Wait()
318
319 runtime.GOMAXPROCS(procs)
320
321 Stop()
322 saveTrace(t, buf, "TestTraceStress")
323 trace := buf.Bytes()
324 parseTrace(t, buf)
325 testBrokenTimestamps(t, trace)
326 }
327
328
329
330
331
332 func isMemoryConstrained() bool {
333 if runtime.GOOS == "plan9" {
334 return true
335 }
336 switch runtime.GOARCH {
337 case "arm", "mips", "mipsle":
338 return true
339 }
340 return false
341 }
342
343
344
345 func TestTraceStressStartStop(t *testing.T) {
346 if runtime.GOOS == "js" {
347 t.Skip("no os.Pipe on js")
348 }
349 if IsEnabled() {
350 t.Skip("skipping because -test.trace is set")
351 }
352 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
353 outerDone := make(chan bool)
354
355 go func() {
356 defer func() {
357 outerDone <- true
358 }()
359
360 var wg sync.WaitGroup
361 done := make(chan bool)
362
363 wg.Add(1)
364 go func() {
365 <-done
366 wg.Done()
367 }()
368
369 rp, wp, err := os.Pipe()
370 if err != nil {
371 t.Errorf("failed to create pipe: %v", err)
372 return
373 }
374 defer func() {
375 rp.Close()
376 wp.Close()
377 }()
378 wg.Add(1)
379 go func() {
380 var tmp [1]byte
381 rp.Read(tmp[:])
382 <-done
383 wg.Done()
384 }()
385 time.Sleep(time.Millisecond)
386
387 go func() {
388 runtime.LockOSThread()
389 for {
390 select {
391 case <-done:
392 return
393 default:
394 runtime.Gosched()
395 }
396 }
397 }()
398
399 runtime.GC()
400
401 n := int(1e3)
402 if isMemoryConstrained() {
403
404
405 n = 512
406 }
407 for i := 0; i < n; i++ {
408 _ = make([]byte, 1<<20)
409 }
410
411
412 for p := 0; p < 10; p++ {
413 wg.Add(1)
414 go func() {
415
416 tmp := make([]byte, 1<<16)
417 for i := range tmp {
418 tmp[i]++
419 }
420 _ = tmp
421 <-done
422 wg.Done()
423 }()
424 }
425
426
427 wg.Add(1)
428 go func() {
429 var tmp [1]byte
430 rp.Read(tmp[:])
431 <-done
432 wg.Done()
433 }()
434
435 runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
436
437
438 timerDone := make(chan bool)
439 go func() {
440 time.Sleep(time.Millisecond)
441 timerDone <- true
442 }()
443 <-timerDone
444
445
446 ln, err := net.Listen("tcp", "127.0.0.1:0")
447 if err != nil {
448 t.Errorf("listen failed: %v", err)
449 return
450 }
451 defer ln.Close()
452 go func() {
453 c, err := ln.Accept()
454 if err != nil {
455 return
456 }
457 time.Sleep(time.Millisecond)
458 var buf [1]byte
459 c.Write(buf[:])
460 c.Close()
461 }()
462 c, err := net.Dial("tcp", ln.Addr().String())
463 if err != nil {
464 t.Errorf("dial failed: %v", err)
465 return
466 }
467 var tmp [1]byte
468 c.Read(tmp[:])
469 c.Close()
470
471 go func() {
472 runtime.Gosched()
473 select {}
474 }()
475
476
477 wp.Write(tmp[:])
478 wp.Write(tmp[:])
479 close(done)
480 wg.Wait()
481 }()
482
483 for i := 0; i < 3; i++ {
484 buf := new(bytes.Buffer)
485 if err := Start(buf); err != nil {
486 t.Fatalf("failed to start tracing: %v", err)
487 }
488 time.Sleep(time.Millisecond)
489 Stop()
490 saveTrace(t, buf, "TestTraceStressStartStop")
491 trace := buf.Bytes()
492 parseTrace(t, buf)
493 testBrokenTimestamps(t, trace)
494 }
495 <-outerDone
496 }
497
498 func TestTraceFutileWakeup(t *testing.T) {
499 if IsEnabled() {
500 t.Skip("skipping because -test.trace is set")
501 }
502 buf := new(bytes.Buffer)
503 if err := Start(buf); err != nil {
504 t.Fatalf("failed to start tracing: %v", err)
505 }
506
507 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
508 c0 := make(chan int, 1)
509 c1 := make(chan int, 1)
510 c2 := make(chan int, 1)
511 const procs = 2
512 var done sync.WaitGroup
513 done.Add(4 * procs)
514 for p := 0; p < procs; p++ {
515 const iters = 1e3
516 go func() {
517 for i := 0; i < iters; i++ {
518 runtime.Gosched()
519 c0 <- 0
520 }
521 done.Done()
522 }()
523 go func() {
524 for i := 0; i < iters; i++ {
525 runtime.Gosched()
526 <-c0
527 }
528 done.Done()
529 }()
530 go func() {
531 for i := 0; i < iters; i++ {
532 runtime.Gosched()
533 select {
534 case c1 <- 0:
535 case c2 <- 0:
536 }
537 }
538 done.Done()
539 }()
540 go func() {
541 for i := 0; i < iters; i++ {
542 runtime.Gosched()
543 select {
544 case <-c1:
545 case <-c2:
546 }
547 }
548 done.Done()
549 }()
550 }
551 done.Wait()
552
553 Stop()
554 saveTrace(t, buf, "TestTraceFutileWakeup")
555 events, _ := parseTrace(t, buf)
556
557
558
559 gs := make(map[uint64]int)
560 for _, ev := range events {
561 switch ev.Type {
562 case trace.EvFutileWakeup:
563 t.Fatalf("found EvFutileWakeup event")
564 case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect:
565 if gs[ev.G] == 2 {
566 t.Fatalf("goroutine %v blocked on %v at %v right after start",
567 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
568 }
569 if gs[ev.G] == 1 {
570 t.Fatalf("goroutine %v blocked on %v at %v while blocked",
571 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
572 }
573 gs[ev.G] = 1
574 case trace.EvGoStart:
575 if gs[ev.G] == 1 {
576 gs[ev.G] = 2
577 }
578 default:
579 delete(gs, ev.G)
580 }
581 }
582 }
583
584 func saveTrace(t *testing.T, buf *bytes.Buffer, name string) {
585 if !*saveTraces {
586 return
587 }
588 if err := os.WriteFile(name+".trace", buf.Bytes(), 0600); err != nil {
589 t.Errorf("failed to write trace file: %s", err)
590 }
591 }
592
View as plain text