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.
24 saveTraces = flag.Bool("savetraces", false, "save traces collected by tests")
27 // TestEventBatch tests Flush calls that happen during Start
28 // don't produce corrupted traces.
29 func TestEventBatch(t *testing.T) {
31 t.Skip("skipping in race mode")
34 t.Skip("skipping because -test.trace is set")
37 t.Skip("skipping in short mode")
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++ {
51 t.Run("G="+strconv.Itoa(n), func(t *testing.T) {
55 in := make(chan bool, 1000)
56 for i := 0; i < n; i++ {
62 buf := new(bytes.Buffer)
63 if err := Start(buf); err != nil {
64 t.Fatalf("failed to start tracing: %v", err)
67 for i := 0; i < n; i++ {
73 _, err := trace.Parse(buf, "")
74 if err == trace.ErrTimeOrder {
75 t.Skipf("skipping trace: %v", err)
79 t.Fatalf("failed to parse trace: %v", err)
85 func TestTraceStartStop(t *testing.T) {
87 t.Skip("skipping because -test.trace is set")
89 buf := new(bytes.Buffer)
90 if err := Start(buf); err != nil {
91 t.Fatalf("failed to start tracing: %v", err)
96 t.Fatalf("trace is empty")
98 time.Sleep(100 * time.Millisecond)
99 if size != buf.Len() {
100 t.Fatalf("trace writes after stop: %v -> %v", size, buf.Len())
102 saveTrace(t, buf, "TestTraceStartStop")
105 func TestTraceDoubleStart(t *testing.T) {
107 t.Skip("skipping because -test.trace is set")
110 buf := new(bytes.Buffer)
111 if err := Start(buf); err != nil {
112 t.Fatalf("failed to start tracing: %v", err)
114 if err := Start(buf); err == nil {
115 t.Fatalf("succeed to start tracing second time")
121 func TestTrace(t *testing.T) {
123 t.Skip("skipping because -test.trace is set")
125 buf := new(bytes.Buffer)
126 if err := Start(buf); err != nil {
127 t.Fatalf("failed to start tracing: %v", err)
130 saveTrace(t, buf, "TestTrace")
131 _, err := trace.Parse(buf, "")
132 if err == trace.ErrTimeOrder {
133 t.Skipf("skipping trace: %v", err)
136 t.Fatalf("failed to parse trace: %v", err)
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)
146 t.Fatalf("failed to parse trace: %v", err)
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)
154 return res.Events, gs
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
168 trace.BreakTimestampsForTesting = false
170 for i := 0; i < 1e4; i++ {
171 _, err := trace.Parse(bytes.NewReader(data), "")
172 if err == trace.ErrTimeOrder {
176 t.Fatalf("failed to parse trace: %v", err)
181 func TestTraceStress(t *testing.T) {
182 if runtime.GOOS == "js" {
183 t.Skip("no os.Pipe on js")
186 t.Skip("skipping because -test.trace is set")
189 t.Skip("skipping in -short mode")
192 var wg sync.WaitGroup
193 done := make(chan bool)
195 // Create a goroutine blocked before tracing.
202 // Create a goroutine blocked in syscall before tracing.
203 rp, wp, err := os.Pipe()
205 t.Fatalf("failed to create pipe: %v", err)
218 time.Sleep(time.Millisecond) // give the goroutine above time to block
220 buf := new(bytes.Buffer)
221 if err := Start(buf); err != nil {
222 t.Fatalf("failed to start tracing: %v", err)
225 procs := runtime.GOMAXPROCS(10)
226 time.Sleep(50 * time.Millisecond) // test proc stop/start events
229 runtime.LockOSThread()
241 // Trigger GC from malloc.
243 if isMemoryConstrained() {
244 // Reduce allocation to avoid running out of
245 // memory on the builder - see issue/12032.
248 for i := 0; i < n; i++ {
249 _ = make([]byte, 1<<20)
252 // Create a bunch of busy goroutines to load all Ps.
253 for p := 0; p < 10; p++ {
256 // Do something useful.
257 tmp := make([]byte, 1<<16)
277 timerDone := make(chan bool)
279 time.Sleep(time.Millisecond)
285 ln, err := net.Listen("tcp", "127.0.0.1:0")
287 t.Fatalf("listen failed: %v", err)
291 c, err := ln.Accept()
295 time.Sleep(time.Millisecond)
300 c, err := net.Dial("tcp", ln.Addr().String())
302 t.Fatalf("dial failed: %v", err)
313 // Unblock helper goroutines and wait them to finish.
319 runtime.GOMAXPROCS(procs)
322 saveTrace(t, buf, "TestTraceStress")
325 testBrokenTimestamps(t, trace)
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" {
336 switch runtime.GOARCH {
337 case "arm", "mips", "mipsle":
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")
350 t.Skip("skipping because -test.trace is set")
352 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
353 outerDone := make(chan bool)
360 var wg sync.WaitGroup
361 done := make(chan bool)
369 rp, wp, err := os.Pipe()
371 t.Errorf("failed to create pipe: %v", err)
385 time.Sleep(time.Millisecond)
388 runtime.LockOSThread()
400 // Trigger GC from malloc.
402 if isMemoryConstrained() {
403 // Reduce allocation to avoid running out of
404 // memory on the builder.
407 for i := 0; i < n; i++ {
408 _ = make([]byte, 1<<20)
411 // Create a bunch of busy goroutines to load all Ps.
412 for p := 0; p < 10; p++ {
415 // Do something useful.
416 tmp := make([]byte, 1<<16)
435 runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
438 timerDone := make(chan bool)
440 time.Sleep(time.Millisecond)
446 ln, err := net.Listen("tcp", "127.0.0.1:0")
448 t.Errorf("listen failed: %v", err)
453 c, err := ln.Accept()
457 time.Sleep(time.Millisecond)
462 c, err := net.Dial("tcp", ln.Addr().String())
464 t.Errorf("dial failed: %v", err)
476 // Unblock helper goroutines and wait them to finish.
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)
488 time.Sleep(time.Millisecond)
490 saveTrace(t, buf, "TestTraceStressStartStop")
493 testBrokenTimestamps(t, trace)
498 func TestTraceFutileWakeup(t *testing.T) {
500 t.Skip("skipping because -test.trace is set")
502 buf := new(bytes.Buffer)
503 if err := Start(buf); err != nil {
504 t.Fatalf("failed to start tracing: %v", err)
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)
512 var done sync.WaitGroup
514 for p := 0; p < procs; p++ {
517 for i := 0; i < iters; i++ {
524 for i := 0; i < iters; i++ {
531 for i := 0; i < iters; i++ {
541 for i := 0; i < iters; i++ {
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 {
562 case trace.EvFutileWakeup:
563 t.Fatalf("found EvFutileWakeup event")
564 case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect:
566 t.Fatalf("goroutine %v blocked on %v at %v right after start",
567 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
570 t.Fatalf("goroutine %v blocked on %v at %v while blocked",
571 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
574 case trace.EvGoStart:
584 func saveTrace(t *testing.T, buf *bytes.Buffer, name string) {
588 if err := os.WriteFile(name+".trace", buf.Bytes(), 0600); err != nil {
589 t.Errorf("failed to write trace file: %s", err)