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.
29 saveTraces = flag.Bool("savetraces", false, "save traces collected by tests")
32 // TestEventBatch tests Flush calls that happen during Start
33 // don't produce corrupted traces.
34 func TestEventBatch(t *testing.T) {
36 t.Skip("skipping in race mode")
39 t.Skip("skipping because -test.trace is set")
42 t.Skip("skipping in short mode")
44 // During Start, bunch of records are written to reflect the current
45 // snapshot of the program, including state of each goroutines.
46 // And some string constants are written to the trace to aid trace
47 // parsing. This test checks Flush of the buffer occurred during
48 // this process doesn't cause corrupted traces.
49 // When a Flush is called during Start is complicated
50 // so we test with a range of number of goroutines hoping that one
51 // of them triggers Flush.
52 // This range was chosen to fill up a ~64KB buffer with traceEvGoCreate
53 // and traceEvGoWaiting events (12~13bytes per goroutine).
54 for g := 4950; g < 5050; g++ {
56 t.Run("G="+strconv.Itoa(n), func(t *testing.T) {
60 in := make(chan bool, 1000)
61 for i := 0; i < n; i++ {
67 buf := new(bytes.Buffer)
68 if err := Start(buf); err != nil {
69 t.Fatalf("failed to start tracing: %v", err)
72 for i := 0; i < n; i++ {
78 _, err := trace.Parse(buf, "")
79 if err == trace.ErrTimeOrder {
80 t.Skipf("skipping trace: %v", err)
84 t.Fatalf("failed to parse trace: %v", err)
90 func TestTraceStartStop(t *testing.T) {
92 t.Skip("skipping because -test.trace is set")
94 buf := new(bytes.Buffer)
95 if err := Start(buf); err != nil {
96 t.Fatalf("failed to start tracing: %v", err)
101 t.Fatalf("trace is empty")
103 time.Sleep(100 * time.Millisecond)
104 if size != buf.Len() {
105 t.Fatalf("trace writes after stop: %v -> %v", size, buf.Len())
107 saveTrace(t, buf, "TestTraceStartStop")
110 func TestTraceDoubleStart(t *testing.T) {
112 t.Skip("skipping because -test.trace is set")
115 buf := new(bytes.Buffer)
116 if err := Start(buf); err != nil {
117 t.Fatalf("failed to start tracing: %v", err)
119 if err := Start(buf); err == nil {
120 t.Fatalf("succeed to start tracing second time")
126 func TestTrace(t *testing.T) {
128 t.Skip("skipping because -test.trace is set")
130 buf := new(bytes.Buffer)
131 if err := Start(buf); err != nil {
132 t.Fatalf("failed to start tracing: %v", err)
135 saveTrace(t, buf, "TestTrace")
136 _, err := trace.Parse(buf, "")
137 if err == trace.ErrTimeOrder {
138 t.Skipf("skipping trace: %v", err)
141 t.Fatalf("failed to parse trace: %v", err)
145 func parseTrace(t *testing.T, r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc) {
146 res, err := trace.Parse(r, "")
147 if err == trace.ErrTimeOrder {
148 t.Skipf("skipping trace: %v", err)
151 t.Fatalf("failed to parse trace: %v", err)
153 gs := trace.GoroutineStats(res.Events)
154 for goid := range gs {
155 // We don't do any particular checks on the result at the moment.
156 // But still check that RelatedGoroutines does not crash, hang, etc.
157 _ = trace.RelatedGoroutines(res.Events, goid)
159 return res.Events, gs
162 func testBrokenTimestamps(t *testing.T, data []byte) {
163 // On some processors cputicks (used to generate trace timestamps)
164 // produce non-monotonic timestamps. It is important that the parser
165 // distinguishes logically inconsistent traces (e.g. missing, excessive
166 // or misordered events) from broken timestamps. The former is a bug
167 // in tracer, the latter is a machine issue.
168 // So now that we have a consistent trace, test that (1) parser does
169 // not return a logical error in case of broken timestamps
170 // and (2) broken timestamps are eventually detected and reported.
171 trace.BreakTimestampsForTesting = true
173 trace.BreakTimestampsForTesting = false
175 for i := 0; i < 1e4; i++ {
176 _, err := trace.Parse(bytes.NewReader(data), "")
177 if err == trace.ErrTimeOrder {
181 t.Fatalf("failed to parse trace: %v", err)
186 func TestTraceStress(t *testing.T) {
187 switch runtime.GOOS {
189 t.Skip("no os.Pipe on " + runtime.GOOS)
192 t.Skip("skipping because -test.trace is set")
195 t.Skip("skipping in -short mode")
198 var wg sync.WaitGroup
199 done := make(chan bool)
201 // Create a goroutine blocked before tracing.
208 // Create a goroutine blocked in syscall before tracing.
209 rp, wp, err := os.Pipe()
211 t.Fatalf("failed to create pipe: %v", err)
224 time.Sleep(time.Millisecond) // give the goroutine above time to block
226 buf := new(bytes.Buffer)
227 if err := Start(buf); err != nil {
228 t.Fatalf("failed to start tracing: %v", err)
231 procs := runtime.GOMAXPROCS(10)
232 time.Sleep(50 * time.Millisecond) // test proc stop/start events
235 runtime.LockOSThread()
247 // Trigger GC from malloc.
249 if isMemoryConstrained() {
250 // Reduce allocation to avoid running out of
251 // memory on the builder - see issue/12032.
254 for i := 0; i < n; i++ {
255 _ = make([]byte, 1<<20)
258 // Create a bunch of busy goroutines to load all Ps.
259 for p := 0; p < 10; p++ {
262 // Do something useful.
263 tmp := make([]byte, 1<<16)
283 timerDone := make(chan bool)
285 time.Sleep(time.Millisecond)
291 ln, err := net.Listen("tcp", "127.0.0.1:0")
293 t.Fatalf("listen failed: %v", err)
297 c, err := ln.Accept()
301 time.Sleep(time.Millisecond)
306 c, err := net.Dial("tcp", ln.Addr().String())
308 t.Fatalf("dial failed: %v", err)
319 // Unblock helper goroutines and wait them to finish.
325 runtime.GOMAXPROCS(procs)
328 saveTrace(t, buf, "TestTraceStress")
331 testBrokenTimestamps(t, trace)
334 // isMemoryConstrained reports whether the current machine is likely
335 // to be memory constrained.
336 // This was originally for the openbsd/arm builder (Issue 12032).
337 // TODO: move this to testenv? Make this look at memory? Look at GO_BUILDER_NAME?
338 func isMemoryConstrained() bool {
339 if runtime.GOOS == "plan9" {
342 switch runtime.GOARCH {
343 case "arm", "mips", "mipsle":
349 // Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine.
350 // And concurrently with all that start/stop trace 3 times.
351 func TestTraceStressStartStop(t *testing.T) {
352 switch runtime.GOOS {
354 t.Skip("no os.Pipe on " + runtime.GOOS)
357 t.Skip("skipping because -test.trace is set")
359 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
360 outerDone := make(chan bool)
367 var wg sync.WaitGroup
368 done := make(chan bool)
376 rp, wp, err := os.Pipe()
378 t.Errorf("failed to create pipe: %v", err)
392 time.Sleep(time.Millisecond)
395 runtime.LockOSThread()
407 // Trigger GC from malloc.
409 if isMemoryConstrained() {
410 // Reduce allocation to avoid running out of
411 // memory on the builder.
414 for i := 0; i < n; i++ {
415 _ = make([]byte, 1<<20)
418 // Create a bunch of busy goroutines to load all Ps.
419 for p := 0; p < 10; p++ {
422 // Do something useful.
423 tmp := make([]byte, 1<<16)
442 runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
445 timerDone := make(chan bool)
447 time.Sleep(time.Millisecond)
453 ln, err := net.Listen("tcp", "127.0.0.1:0")
455 t.Errorf("listen failed: %v", err)
460 c, err := ln.Accept()
464 time.Sleep(time.Millisecond)
469 c, err := net.Dial("tcp", ln.Addr().String())
471 t.Errorf("dial failed: %v", err)
483 // Unblock helper goroutines and wait them to finish.
490 for i := 0; i < 3; i++ {
491 buf := new(bytes.Buffer)
492 if err := Start(buf); err != nil {
493 t.Fatalf("failed to start tracing: %v", err)
495 time.Sleep(time.Millisecond)
497 saveTrace(t, buf, "TestTraceStressStartStop")
500 testBrokenTimestamps(t, trace)
505 func TestTraceFutileWakeup(t *testing.T) {
507 t.Skip("skipping because -test.trace is set")
509 buf := new(bytes.Buffer)
510 if err := Start(buf); err != nil {
511 t.Fatalf("failed to start tracing: %v", err)
514 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
515 c0 := make(chan int, 1)
516 c1 := make(chan int, 1)
517 c2 := make(chan int, 1)
519 var done sync.WaitGroup
521 for p := 0; p < procs; p++ {
524 for i := 0; i < iters; i++ {
531 for i := 0; i < iters; i++ {
538 for i := 0; i < iters; i++ {
548 for i := 0; i < iters; i++ {
561 saveTrace(t, buf, "TestTraceFutileWakeup")
562 events, _ := parseTrace(t, buf)
563 // Check that (1) trace does not contain EvFutileWakeup events and
564 // (2) there are no consecutive EvGoBlock/EvGCStart/EvGoBlock events
565 // (we call runtime.Gosched between all operations, so these would be futile wakeups).
566 gs := make(map[uint64]int)
567 for _, ev := range events {
569 case trace.EvFutileWakeup:
570 t.Fatalf("found EvFutileWakeup event")
571 case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect:
573 t.Fatalf("goroutine %v blocked on %v at %v right after start",
574 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
577 t.Fatalf("goroutine %v blocked on %v at %v while blocked",
578 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
581 case trace.EvGoStart:
591 func TestTraceCPUProfile(t *testing.T) {
593 t.Skip("skipping because -test.trace is set")
596 cpuBuf := new(bytes.Buffer)
597 if err := pprof.StartCPUProfile(cpuBuf); err != nil {
598 t.Skipf("failed to start CPU profile: %v", err)
601 buf := new(bytes.Buffer)
602 if err := Start(buf); err != nil {
603 t.Fatalf("failed to start tracing: %v", err)
606 dur := 100 * time.Millisecond
608 // Create a region in the execution trace. Set and clear goroutine
609 // labels fully within that region, so we know that any CPU profile
610 // sample with the label must also be eligible for inclusion in the
612 ctx := context.Background()
613 defer StartRegion(ctx, "cpuHogger").End()
614 pprof.Do(ctx, pprof.Labels("tracing", "on"), func(ctx context.Context) {
615 cpuHogger(cpuHog1, &salt1, dur)
617 // Be sure the execution trace's view, when filtered to this goroutine
618 // via the explicit goroutine ID in each event, gets many more samples
619 // than the CPU profiler when filtered to this goroutine via labels.
620 cpuHogger(cpuHog1, &salt1, dur)
624 pprof.StopCPUProfile()
625 saveTrace(t, buf, "TestTraceCPUProfile")
627 prof, err := profile.Parse(cpuBuf)
629 t.Fatalf("failed to parse CPU profile: %v", err)
631 // Examine the CPU profiler's view. Filter it to only include samples from
632 // the single test goroutine. Use labels to execute that filter: they should
633 // apply to all work done while that goroutine is getg().m.curg, and they
634 // should apply to no other goroutines.
636 pprofStacks := make(map[string]int)
637 for _, s := range prof.Sample {
638 if s.Label["tracing"] != nil {
641 for _, loc := range s.Location {
642 for _, line := range loc.Line {
643 fns = append(fns, fmt.Sprintf("%s:%d", line.Function.Name, line.Line))
644 leaf = line.Function.Name
647 // runtime.sigprof synthesizes call stacks when "normal traceback is
648 // impossible or has failed", using particular placeholder functions
649 // to represent common failure cases. Look for those functions in
650 // the leaf position as a sign that the call stack and its
651 // symbolization are more complex than this test can handle.
653 // TODO: Make the symbolization done by the execution tracer and CPU
654 // profiler match up even in these harder cases. See #53378.
656 case "runtime._System", "runtime._GC", "runtime._ExternalCode", "runtime._VDSO":
659 stack := strings.Join(fns, " ")
660 samples := int(s.Value[0])
661 pprofSamples += samples
662 pprofStacks[stack] += samples
665 if pprofSamples == 0 {
666 t.Skipf("CPU profile did not include any samples while tracing was active\n%s", prof)
669 // Examine the execution tracer's view of the CPU profile samples. Filter it
670 // to only include samples from the single test goroutine. Use the goroutine
671 // ID that was recorded in the events: that should reflect getg().m.curg,
672 // same as the profiler's labels (even when the M is using its g0 stack).
673 totalTraceSamples := 0
675 traceStacks := make(map[string]int)
676 events, _ := parseTrace(t, buf)
677 var hogRegion *trace.Event
678 for _, ev := range events {
679 if ev.Type == trace.EvUserRegion && ev.Args[1] == 0 && ev.SArgs[0] == "cpuHogger" {
680 // mode "0" indicates region start
684 if hogRegion == nil {
685 t.Fatalf("execution trace did not identify cpuHogger goroutine")
686 } else if hogRegion.Link == nil {
687 t.Fatalf("execution trace did not close cpuHogger region")
689 for _, ev := range events {
690 if ev.Type == trace.EvCPUSample {
692 if ev.G == hogRegion.G {
695 for _, frame := range ev.Stk {
696 if frame.Fn != "runtime.goexit" {
697 fns = append(fns, fmt.Sprintf("%s:%d", frame.Fn, frame.Line))
700 stack := strings.Join(fns, " ")
706 // The execution trace may drop CPU profile samples if the profiling buffer
707 // overflows. Based on the size of profBufWordCount, that takes a bit over
708 // 1900 CPU samples or 19 thread-seconds at a 100 Hz sample rate. If we've
709 // hit that case, then we definitely have at least one full buffer's worth
710 // of CPU samples, so we'll call that success.
711 overflowed := totalTraceSamples >= 1900
712 if traceSamples < pprofSamples {
713 t.Logf("execution trace did not include all CPU profile samples; %d in profile, %d in trace", pprofSamples, traceSamples)
719 for stack, traceSamples := range traceStacks {
720 pprofSamples := pprofStacks[stack]
721 delete(pprofStacks, stack)
722 if traceSamples < pprofSamples {
723 t.Logf("execution trace did not include all CPU profile samples for stack %q; %d in profile, %d in trace",
724 stack, pprofSamples, traceSamples)
730 for stack, pprofSamples := range pprofStacks {
731 t.Logf("CPU profile included %d samples at stack %q not present in execution trace", pprofSamples, stack)
738 t.Logf("execution trace CPU samples:")
739 for stack, samples := range traceStacks {
740 t.Logf("%d: %q", samples, stack)
742 t.Logf("CPU profile:\n%v", prof)
746 func cpuHogger(f func(x int) int, y *int, dur time.Duration) {
747 // We only need to get one 100 Hz clock tick, so we've got
748 // a large safety buffer.
749 // But do at least 500 iterations (which should take about 100ms),
750 // otherwise TestCPUProfileMultithreaded can fail if only one
751 // thread is scheduled during the testing period.
754 for i := 0; i < 500 || time.Since(t0) < dur; i++ {
764 // The actual CPU hogging function.
765 // Must not call other functions nor access heap/globals in the loop,
766 // otherwise under race detector the samples will be in the race runtime.
767 func cpuHog1(x int) int {
768 return cpuHog0(x, 1e5)
771 func cpuHog0(x, n int) int {
773 for i := 0; i < n; i++ {
775 // Spend time in mcall, stored as gp.m.curg, with g0 running
787 func saveTrace(t *testing.T, buf *bytes.Buffer, name string) {
791 if err := os.WriteFile(name+".trace", buf.Bytes(), 0600); err != nil {
792 t.Errorf("failed to write trace file: %s", err)