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.
25 saveTraces = flag.Bool("savetraces", false, "save traces collected by tests")
28 // TestEventBatch tests Flush calls that happen during Start
29 // don't produce corrupted traces.
30 func TestEventBatch(t *testing.T) {
32 t.Skip("skipping in race mode")
35 t.Skip("skipping in short mode")
37 // During Start, bunch of records are written to reflect the current
38 // snapshot of the program, including state of each goroutines.
39 // And some string constants are written to the trace to aid trace
40 // parsing. This test checks Flush of the buffer occurred during
41 // this process doesn't cause corrupted traces.
42 // When a Flush is called during Start is complicated
43 // so we test with a range of number of goroutines hoping that one
44 // of them triggers Flush.
45 // This range was chosen to fill up a ~64KB buffer with traceEvGoCreate
46 // and traceEvGoWaiting events (12~13bytes per goroutine).
47 for g := 4950; g < 5050; g++ {
49 t.Run("G="+strconv.Itoa(n), func(t *testing.T) {
53 in := make(chan bool, 1000)
54 for i := 0; i < n; i++ {
60 buf := new(bytes.Buffer)
61 if err := Start(buf); err != nil {
62 t.Fatalf("failed to start tracing: %v", err)
65 for i := 0; i < n; i++ {
71 _, err := trace.Parse(buf, "")
72 if err == trace.ErrTimeOrder {
73 t.Skipf("skipping trace: %v", err)
77 t.Fatalf("failed to parse trace: %v", err)
83 func TestTraceStartStop(t *testing.T) {
84 buf := new(bytes.Buffer)
85 if err := Start(buf); err != nil {
86 t.Fatalf("failed to start tracing: %v", err)
91 t.Fatalf("trace is empty")
93 time.Sleep(100 * time.Millisecond)
94 if size != buf.Len() {
95 t.Fatalf("trace writes after stop: %v -> %v", size, buf.Len())
97 saveTrace(t, buf, "TestTraceStartStop")
100 func TestTraceDoubleStart(t *testing.T) {
102 buf := new(bytes.Buffer)
103 if err := Start(buf); err != nil {
104 t.Fatalf("failed to start tracing: %v", err)
106 if err := Start(buf); err == nil {
107 t.Fatalf("succeed to start tracing second time")
113 func TestTrace(t *testing.T) {
114 buf := new(bytes.Buffer)
115 if err := Start(buf); err != nil {
116 t.Fatalf("failed to start tracing: %v", err)
119 saveTrace(t, buf, "TestTrace")
120 _, err := trace.Parse(buf, "")
121 if err == trace.ErrTimeOrder {
122 t.Skipf("skipping trace: %v", err)
125 t.Fatalf("failed to parse trace: %v", err)
129 func parseTrace(t *testing.T, r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc) {
130 res, err := trace.Parse(r, "")
131 if err == trace.ErrTimeOrder {
132 t.Skipf("skipping trace: %v", err)
135 t.Fatalf("failed to parse trace: %v", err)
137 gs := trace.GoroutineStats(res.Events)
138 for goid := range gs {
139 // We don't do any particular checks on the result at the moment.
140 // But still check that RelatedGoroutines does not crash, hang, etc.
141 _ = trace.RelatedGoroutines(res.Events, goid)
143 return res.Events, gs
146 func testBrokenTimestamps(t *testing.T, data []byte) {
147 // On some processors cputicks (used to generate trace timestamps)
148 // produce non-monotonic timestamps. It is important that the parser
149 // distinguishes logically inconsistent traces (e.g. missing, excessive
150 // or misordered events) from broken timestamps. The former is a bug
151 // in tracer, the latter is a machine issue.
152 // So now that we have a consistent trace, test that (1) parser does
153 // not return a logical error in case of broken timestamps
154 // and (2) broken timestamps are eventually detected and reported.
155 trace.BreakTimestampsForTesting = true
157 trace.BreakTimestampsForTesting = false
159 for i := 0; i < 1e4; i++ {
160 _, err := trace.Parse(bytes.NewReader(data), "")
161 if err == trace.ErrTimeOrder {
165 t.Fatalf("failed to parse trace: %v", err)
170 func TestTraceStress(t *testing.T) {
171 var wg sync.WaitGroup
172 done := make(chan bool)
174 // Create a goroutine blocked before tracing.
181 // Create a goroutine blocked in syscall before tracing.
182 rp, wp, err := os.Pipe()
184 t.Fatalf("failed to create pipe: %v", err)
197 time.Sleep(time.Millisecond) // give the goroutine above time to block
199 buf := new(bytes.Buffer)
200 if err := Start(buf); err != nil {
201 t.Fatalf("failed to start tracing: %v", err)
204 procs := runtime.GOMAXPROCS(10)
205 time.Sleep(50 * time.Millisecond) // test proc stop/start events
208 runtime.LockOSThread()
220 // Trigger GC from malloc.
222 if runtime.GOOS == "openbsd" && runtime.GOARCH == "arm" {
223 // Reduce allocation to avoid running out of
224 // memory on the builder - see issue/12032.
227 for i := 0; i < n; i++ {
228 _ = make([]byte, 1<<20)
231 // Create a bunch of busy goroutines to load all Ps.
232 for p := 0; p < 10; p++ {
235 // Do something useful.
236 tmp := make([]byte, 1<<16)
256 timerDone := make(chan bool)
258 time.Sleep(time.Millisecond)
264 ln, err := net.Listen("tcp", "127.0.0.1:0")
266 t.Fatalf("listen failed: %v", err)
270 c, err := ln.Accept()
274 time.Sleep(time.Millisecond)
279 c, err := net.Dial("tcp", ln.Addr().String())
281 t.Fatalf("dial failed: %v", err)
292 // Unblock helper goroutines and wait them to finish.
298 runtime.GOMAXPROCS(procs)
301 saveTrace(t, buf, "TestTraceStress")
304 testBrokenTimestamps(t, trace)
307 // Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine.
308 // And concurrently with all that start/stop trace 3 times.
309 func TestTraceStressStartStop(t *testing.T) {
310 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
311 outerDone := make(chan bool)
318 var wg sync.WaitGroup
319 done := make(chan bool)
327 rp, wp, err := os.Pipe()
329 t.Errorf("failed to create pipe: %v", err)
343 time.Sleep(time.Millisecond)
346 runtime.LockOSThread()
358 // Trigger GC from malloc.
360 if runtime.GOOS == "openbsd" && runtime.GOARCH == "arm" {
361 // Reduce allocation to avoid running out of
362 // memory on the builder - see issue/12032.
365 for i := 0; i < n; i++ {
366 _ = make([]byte, 1<<20)
369 // Create a bunch of busy goroutines to load all Ps.
370 for p := 0; p < 10; p++ {
373 // Do something useful.
374 tmp := make([]byte, 1<<16)
393 runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
396 timerDone := make(chan bool)
398 time.Sleep(time.Millisecond)
404 ln, err := net.Listen("tcp", "127.0.0.1:0")
406 t.Errorf("listen failed: %v", err)
411 c, err := ln.Accept()
415 time.Sleep(time.Millisecond)
420 c, err := net.Dial("tcp", ln.Addr().String())
422 t.Errorf("dial failed: %v", err)
434 // Unblock helper goroutines and wait them to finish.
441 for i := 0; i < 3; i++ {
442 buf := new(bytes.Buffer)
443 if err := Start(buf); err != nil {
444 t.Fatalf("failed to start tracing: %v", err)
446 time.Sleep(time.Millisecond)
448 saveTrace(t, buf, "TestTraceStressStartStop")
451 testBrokenTimestamps(t, trace)
456 func TestTraceFutileWakeup(t *testing.T) {
457 buf := new(bytes.Buffer)
458 if err := Start(buf); err != nil {
459 t.Fatalf("failed to start tracing: %v", err)
462 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
463 c0 := make(chan int, 1)
464 c1 := make(chan int, 1)
465 c2 := make(chan int, 1)
467 var done sync.WaitGroup
469 for p := 0; p < procs; p++ {
472 for i := 0; i < iters; i++ {
479 for i := 0; i < iters; i++ {
486 for i := 0; i < iters; i++ {
496 for i := 0; i < iters; i++ {
509 saveTrace(t, buf, "TestTraceFutileWakeup")
510 events, _ := parseTrace(t, buf)
511 // Check that (1) trace does not contain EvFutileWakeup events and
512 // (2) there are no consecutive EvGoBlock/EvGCStart/EvGoBlock events
513 // (we call runtime.Gosched between all operations, so these would be futile wakeups).
514 gs := make(map[uint64]int)
515 for _, ev := range events {
517 case trace.EvFutileWakeup:
518 t.Fatalf("found EvFutileWakeup event")
519 case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect:
521 t.Fatalf("goroutine %v blocked on %v at %v right after start",
522 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
525 t.Fatalf("goroutine %v blocked on %v at %v while blocked",
526 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
529 case trace.EvGoStart:
539 func saveTrace(t *testing.T, buf *bytes.Buffer, name string) {
543 if err := ioutil.WriteFile(name+".trace", buf.Bytes(), 0600); err != nil {
544 t.Errorf("failed to write trace file: %s", err)