func TestTrace(t *testing.T) { skipTraceTestsIfNeeded(t) buf := new(bytes.Buffer) if err := StartTrace(buf); err != nil { t.Fatalf("failed to start tracing: %v", err) } StopTrace() _, err := trace.Parse(buf) if err != nil { t.Fatalf("failed to parse trace: %v", err) } }
func Fuzz(data []byte) int { events, err := trace.Parse(bytes.NewReader(data)) if err != nil { if events != nil { panic("events is not nil on error") } return 0 } trace.GoroutineStats(events) trace.RelatedGoroutines(events, 1) return 1 }
func parseTrace(r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc, error) { events, err := trace.Parse(r) if err != nil { return nil, nil, err } gs := trace.GoroutineStats(events) for goid := range gs { // We don't do any particular checks on the result at the moment. // But still check that RelatedGoroutines does not crash, hang, etc. _ = trace.RelatedGoroutines(events, goid) } return events, gs, nil }
// TestGoroutineCount tests runnable/running goroutine counts computed by generateTrace // remain in the valid range. // - the counts must not be negative. generateTrace will return an error. // - the counts must not include goroutines blocked waiting on channels or in syscall. func TestGoroutineCount(t *testing.T) { w := trace.NewWriter() w.Emit(trace.EvBatch, 0, 0) // start of per-P batch event [pid, timestamp] w.Emit(trace.EvFrequency, 1) // [ticks per second] // In this test, we assume a valid trace contains EvGoWaiting or EvGoInSyscall // event for every blocked goroutine. // goroutine 10: blocked w.Emit(trace.EvGoCreate, 1, 10, 1, 1) // [timestamp, new goroutine id, new stack id, stack id] w.Emit(trace.EvGoWaiting, 1, 10) // [timestamp, goroutine id] // goroutine 20: in syscall w.Emit(trace.EvGoCreate, 1, 20, 2, 1) w.Emit(trace.EvGoInSyscall, 1, 20) // [timestamp, goroutine id] // goroutine 30: runnable w.Emit(trace.EvGoCreate, 1, 30, 5, 1) w.Emit(trace.EvProcStart, 2, 0) // [timestamp, thread id] // goroutine 40: runnable->running->runnable w.Emit(trace.EvGoCreate, 1, 40, 7, 1) w.Emit(trace.EvGoStartLocal, 1, 40) // [timestamp, goroutine id] w.Emit(trace.EvGoSched, 1, 8) // [timestamp, stack] events, err := trace.Parse(w, "") if err != nil { t.Fatalf("failed to parse test trace: %v", err) } params := &traceParams{ events: events, endTime: int64(1<<63 - 1), } // If the counts drop below 0, generateTrace will return an error. viewerData, err := generateTrace(params) if err != nil { t.Fatalf("generateTrace failed: %v", err) } for _, ev := range viewerData.Events { if ev.Name == "Goroutines" { cnt := ev.Arg.(*goroutineCountersArg) if cnt.Runnable+cnt.Running > 2 { t.Errorf("goroutine count=%+v; want no more than 2 goroutines in runnable/running state", cnt) } t.Logf("read %+v %+v", ev, cnt) } } }
func TestTrace(t *testing.T) { buf := new(bytes.Buffer) if err := Start(buf); err != nil { t.Fatalf("failed to start tracing: %v", err) } Stop() _, err := trace.Parse(buf) if err == trace.ErrTimeOrder { t.Skipf("skipping trace: %v", err) } if err != nil { t.Fatalf("failed to parse trace: %v", err) } }
func parseTrace(t *testing.T, r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc) { events, err := trace.Parse(r, "") if err == trace.ErrTimeOrder { t.Skipf("skipping trace: %v", err) } if err != nil { t.Fatalf("failed to parse trace: %v", err) } gs := trace.GoroutineStats(events) for goid := range gs { // We don't do any particular checks on the result at the moment. // But still check that RelatedGoroutines does not crash, hang, etc. _ = trace.RelatedGoroutines(events, goid) } return events, gs }
func parseEvents() ([]*trace.Event, error) { loader.once.Do(func() { tracef, err := os.Open(traceFile) if err != nil { loader.err = fmt.Errorf("failed to open trace file: %v", err) return } defer tracef.Close() // Parse and symbolize. events, err := trace.Parse(bufio.NewReader(tracef), programBinary) if err != nil { loader.err = fmt.Errorf("failed to parse trace: %v", err) return } loader.events = events }) return loader.events, loader.err }
func TestGoroutineFilter(t *testing.T) { // Test that we handle state changes to selected goroutines // caused by events on goroutines that are not selected. w := trace.NewWriter() w.Emit(trace.EvBatch, 0, 0) // start of per-P batch event [pid, timestamp] w.Emit(trace.EvFrequency, 1) // [ticks per second] // goroutine 10: blocked w.Emit(trace.EvGoCreate, 1, 10, 1, 1) // [timestamp, new goroutine id, new stack id, stack id] w.Emit(trace.EvGoWaiting, 1, 10) // [timestamp, goroutine id] // goroutine 20: runnable->running->unblock 10 w.Emit(trace.EvGoCreate, 1, 20, 7, 1) w.Emit(trace.EvGoStartLocal, 1, 20) // [timestamp, goroutine id] w.Emit(trace.EvGoUnblockLocal, 1, 10, 8) // [timestamp, goroutine id, stack] w.Emit(trace.EvGoEnd, 1) // [timestamp] // goroutine 10: runnable->running->block w.Emit(trace.EvGoStartLocal, 1, 10) // [timestamp, goroutine id] w.Emit(trace.EvGoBlock, 1, 9) // [timestamp, stack] events, err := trace.Parse(w, "") if err != nil { t.Fatalf("failed to parse test trace: %v", err) } params := &traceParams{ events: events, endTime: int64(1<<63 - 1), gs: map[uint64]bool{10: true}, } _, err = generateTrace(params) if err != nil { t.Fatalf("generateTrace failed: %v", err) } }
func testBrokenTimestamps(t *testing.T, data []byte) { // On some processors cputicks (used to generate trace timestamps) // produce non-monotonic timestamps. It is important that the parser // distinguishes logically inconsistent traces (e.g. missing, excessive // or misordered events) from broken timestamps. The former is a bug // in tracer, the latter is a machine issue. // So now that we have a consistent trace, test that (1) parser does // not return a logical error in case of broken timestamps // and (2) broken timestamps are eventually detected and reported. trace.BreakTimestampsForTesting = true defer func() { trace.BreakTimestampsForTesting = false }() for i := 0; i < 1e4; i++ { _, err := trace.Parse(bytes.NewReader(data), "") if err == trace.ErrTimeOrder { return } if err != nil { t.Fatalf("failed to parse trace: %v", err) } } }