]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/trace/trace_test.go
[dev.typeparams] merge: merge branch 'dev.regabi' into 'dev.typeparams'
[gostls13.git] / src / runtime / trace / trace_test.go
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
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 // TestEventBatch tests Flush calls that happen during Start
28 // don't produce corrupted traces.
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         // During Start, bunch of records are written to reflect the current
40         // snapshot of the program, including state of each goroutines.
41         // And some string constants are written to the trace to aid trace
42         // parsing. This test checks Flush of the buffer occurred during
43         // this process doesn't cause corrupted traces.
44         // When a Flush is called during Start is complicated
45         // so we test with a range of number of goroutines hoping that one
46         // of them triggers Flush.
47         // This range was chosen to fill up a ~64KB buffer with traceEvGoCreate
48         // and traceEvGoWaiting events (12~13bytes per goroutine).
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                 // We don't do any particular checks on the result at the moment.
151                 // But still check that RelatedGoroutines does not crash, hang, etc.
152                 _ = trace.RelatedGoroutines(res.Events, goid)
153         }
154         return res.Events, gs
155 }
156
157 func testBrokenTimestamps(t *testing.T, data []byte) {
158         // On some processors cputicks (used to generate trace timestamps)
159         // produce non-monotonic timestamps. It is important that the parser
160         // distinguishes logically inconsistent traces (e.g. missing, excessive
161         // or misordered events) from broken timestamps. The former is a bug
162         // in tracer, the latter is a machine issue.
163         // So now that we have a consistent trace, test that (1) parser does
164         // not return a logical error in case of broken timestamps
165         // and (2) broken timestamps are eventually detected and reported.
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         // Create a goroutine blocked before tracing.
196         wg.Add(1)
197         go func() {
198                 <-done
199                 wg.Done()
200         }()
201
202         // Create a goroutine blocked in syscall before tracing.
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) // give the goroutine above time to block
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) // test proc stop/start events
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         // Trigger GC from malloc.
242         n := int(1e3)
243         if isMemoryConstrained() {
244                 // Reduce allocation to avoid running out of
245                 // memory on the builder - see issue/12032.
246                 n = 512
247         }
248         for i := 0; i < n; i++ {
249                 _ = make([]byte, 1<<20)
250         }
251
252         // Create a bunch of busy goroutines to load all Ps.
253         for p := 0; p < 10; p++ {
254                 wg.Add(1)
255                 go func() {
256                         // Do something useful.
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         // Block in syscall.
268         wg.Add(1)
269         go func() {
270                 var tmp [1]byte
271                 rp.Read(tmp[:])
272                 <-done
273                 wg.Done()
274         }()
275
276         // Test timers.
277         timerDone := make(chan bool)
278         go func() {
279                 time.Sleep(time.Millisecond)
280                 timerDone <- true
281         }()
282         <-timerDone
283
284         // A bit of network.
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         // Unblock helper goroutines and wait them to finish.
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 // isMemoryConstrained reports whether the current machine is likely
329 // to be memory constrained.
330 // This was originally for the openbsd/arm builder (Issue 12032).
331 // TODO: move this to testenv? Make this look at memory? Look at GO_BUILDER_NAME?
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 // Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine.
344 // And concurrently with all that start/stop trace 3 times.
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                 // Trigger GC from malloc.
401                 n := int(1e3)
402                 if isMemoryConstrained() {
403                         // Reduce allocation to avoid running out of
404                         // memory on the builder.
405                         n = 512
406                 }
407                 for i := 0; i < n; i++ {
408                         _ = make([]byte, 1<<20)
409                 }
410
411                 // Create a bunch of busy goroutines to load all Ps.
412                 for p := 0; p < 10; p++ {
413                         wg.Add(1)
414                         go func() {
415                                 // Do something useful.
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                 // Block in syscall.
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                 // Test timers.
438                 timerDone := make(chan bool)
439                 go func() {
440                         time.Sleep(time.Millisecond)
441                         timerDone <- true
442                 }()
443                 <-timerDone
444
445                 // A bit of network.
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                 // Unblock helper goroutines and wait them to finish.
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         // Check that (1) trace does not contain EvFutileWakeup events and
557         // (2) there are no consecutive EvGoBlock/EvGCStart/EvGoBlock events
558         // (we call runtime.Gosched between all operations, so these would be futile wakeups).
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 }