1 package main 2 3 import ( 4 "internal/trace" 5 "testing" 6 ) 7 8 // TestGoroutineCount tests runnable/running goroutine counts computed by generateTrace 9 // remain in the valid range. 10 // - the counts must not be negative. generateTrace will return an error. 11 // - the counts must not include goroutines blocked waiting on channels or in syscall. 12 func TestGoroutineCount(t *testing.T) { 13 w := trace.NewWriter() 14 w.Emit(trace.EvBatch, 0, 0) // start of per-P batch event [pid, timestamp] 15 w.Emit(trace.EvFrequency, 1) // [ticks per second] 16 17 // In this test, we assume a valid trace contains EvGoWaiting or EvGoInSyscall 18 // event for every blocked goroutine. 19 20 // goroutine 10: blocked 21 w.Emit(trace.EvGoCreate, 1, 10, 1, 1) // [timestamp, new goroutine id, new stack id, stack id] 22 w.Emit(trace.EvGoWaiting, 1, 10) // [timestamp, goroutine id] 23 24 // goroutine 20: in syscall 25 w.Emit(trace.EvGoCreate, 1, 20, 2, 1) 26 w.Emit(trace.EvGoInSyscall, 1, 20) // [timestamp, goroutine id] 27 28 // goroutine 30: runnable 29 w.Emit(trace.EvGoCreate, 1, 30, 5, 1) 30 31 w.Emit(trace.EvProcStart, 2, 0) // [timestamp, thread id] 32 33 // goroutine 40: runnable->running->runnable 34 w.Emit(trace.EvGoCreate, 1, 40, 7, 1) 35 w.Emit(trace.EvGoStartLocal, 1, 40) // [timestamp, goroutine id] 36 w.Emit(trace.EvGoSched, 1, 8) // [timestamp, stack] 37 38 events, err := trace.Parse(w, "") 39 if err != nil { 40 t.Fatalf("failed to parse test trace: %v", err) 41 } 42 43 params := &traceParams{ 44 events: events, 45 endTime: int64(1<<63 - 1), 46 } 47 48 // If the counts drop below 0, generateTrace will return an error. 49 viewerData, err := generateTrace(params) 50 if err != nil { 51 t.Fatalf("generateTrace failed: %v", err) 52 } 53 for _, ev := range viewerData.Events { 54 if ev.Name == "Goroutines" { 55 cnt := ev.Arg.(*goroutineCountersArg) 56 if cnt.Runnable+cnt.Running > 2 { 57 t.Errorf("goroutine count=%+v; want no more than 2 goroutines in runnable/running state", cnt) 58 } 59 t.Logf("read %+v %+v", ev, cnt) 60 } 61 } 62 } 63 64 func TestGoroutineFilter(t *testing.T) { 65 // Test that we handle state changes to selected goroutines 66 // caused by events on goroutines that are not selected. 67 68 w := trace.NewWriter() 69 w.Emit(trace.EvBatch, 0, 0) // start of per-P batch event [pid, timestamp] 70 w.Emit(trace.EvFrequency, 1) // [ticks per second] 71 72 // goroutine 10: blocked 73 w.Emit(trace.EvGoCreate, 1, 10, 1, 1) // [timestamp, new goroutine id, new stack id, stack id] 74 w.Emit(trace.EvGoWaiting, 1, 10) // [timestamp, goroutine id] 75 76 // goroutine 20: runnable->running->unblock 10 77 w.Emit(trace.EvGoCreate, 1, 20, 7, 1) 78 w.Emit(trace.EvGoStartLocal, 1, 20) // [timestamp, goroutine id] 79 w.Emit(trace.EvGoUnblockLocal, 1, 10, 8) // [timestamp, goroutine id, stack] 80 w.Emit(trace.EvGoEnd, 1) // [timestamp] 81 82 // goroutine 10: runnable->running->block 83 w.Emit(trace.EvGoStartLocal, 1, 10) // [timestamp, goroutine id] 84 w.Emit(trace.EvGoBlock, 1, 9) // [timestamp, stack] 85 86 events, err := trace.Parse(w, "") 87 if err != nil { 88 t.Fatalf("failed to parse test trace: %v", err) 89 } 90 91 params := &traceParams{ 92 events: events, 93 endTime: int64(1<<63 - 1), 94 gs: map[uint64]bool{10: true}, 95 } 96 97 _, err = generateTrace(params) 98 if err != nil { 99 t.Fatalf("generateTrace failed: %v", err) 100 } 101 } 102