1
0
mirror of https://github.com/golang/go synced 2024-10-02 06:28:33 -06:00

runtime/trace: add stack tests for GOMAXPROCS

and reorganize test log messages for stack dumps
for easier debugging.

The error log will be formatted like the following:

	trace_stack_test.go:282: Did not match event GoCreate with stack
		 runtime/trace_test.TestTraceSymbolize	 :39
		 testing.tRunner			 :0

		Seen 30 events of the type
		Offset 1890
		 runtime/trace_test.TestTraceSymbolize	/go/src/runtime/trace/trace_stack_test.go:30
		 testing.tRunner			/go/src/testing/testing.go:777
		Offset 1899
		 runtime/trace_test.TestTraceSymbolize	/go/src/runtime/trace/trace_stack_test.go:30
		 testing.tRunner			/go/src/testing/testing.go:777
		 ...

Change-Id: I0468de04507d6ae38ba84d99d13f7bf592e8d115
Reviewed-on: https://go-review.googlesource.com/92916
Reviewed-by: Heschi Kreinick <heschi@google.com>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
Hana Kim 2018-02-08 13:23:48 -05:00 committed by Hyang-Ah Hana Kim
parent df5997b99b
commit ebd04885c8

View File

@ -6,14 +6,17 @@ package trace_test
import ( import (
"bytes" "bytes"
"fmt"
"internal/testenv" "internal/testenv"
"internal/trace" "internal/trace"
"net" "net"
"os" "os"
"runtime" "runtime"
. "runtime/trace" . "runtime/trace"
"strings"
"sync" "sync"
"testing" "testing"
"text/tabwriter"
"time" "time"
) )
@ -34,28 +37,28 @@ func TestTraceSymbolize(t *testing.T) {
// on a channel, in a select or otherwise. So we kick off goroutines // on a channel, in a select or otherwise. So we kick off goroutines
// that need to block first in the hope that while we are executing // that need to block first in the hope that while we are executing
// the rest of the test, they will block. // the rest of the test, they will block.
go func() { go func() { // func1
select {} select {}
}() }()
go func() { go func() { // func2
var c chan int var c chan int
c <- 0 c <- 0
}() }()
go func() { go func() { // func3
var c chan int var c chan int
<-c <-c
}() }()
done1 := make(chan bool) done1 := make(chan bool)
go func() { go func() { // func4
<-done1 <-done1
}() }()
done2 := make(chan bool) done2 := make(chan bool)
go func() { go func() { // func5
done2 <- true done2 <- true
}() }()
c1 := make(chan int) c1 := make(chan int)
c2 := make(chan int) c2 := make(chan int)
go func() { go func() { // func6
select { select {
case <-c1: case <-c1:
case <-c2: case <-c2:
@ -63,17 +66,17 @@ func TestTraceSymbolize(t *testing.T) {
}() }()
var mu sync.Mutex var mu sync.Mutex
mu.Lock() mu.Lock()
go func() { go func() { // func7
mu.Lock() mu.Lock()
mu.Unlock() mu.Unlock()
}() }()
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Add(1)
go func() { go func() { // func8
wg.Wait() wg.Wait()
}() }()
cv := sync.NewCond(&sync.Mutex{}) cv := sync.NewCond(&sync.Mutex{})
go func() { go func() { // func9
cv.L.Lock() cv.L.Lock()
cv.Wait() cv.Wait()
cv.L.Unlock() cv.L.Unlock()
@ -82,7 +85,7 @@ func TestTraceSymbolize(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to listen: %v", err) t.Fatalf("failed to listen: %v", err)
} }
go func() { go func() { // func10
c, err := ln.Accept() c, err := ln.Accept()
if err != nil { if err != nil {
t.Errorf("failed to accept: %v", err) t.Errorf("failed to accept: %v", err)
@ -97,7 +100,7 @@ func TestTraceSymbolize(t *testing.T) {
defer rp.Close() defer rp.Close()
defer wp.Close() defer wp.Close()
pipeReadDone := make(chan bool) pipeReadDone := make(chan bool)
go func() { go func() { // func11
var data [1]byte var data [1]byte
rp.Read(data[:]) rp.Read(data[:])
pipeReadDone <- true pipeReadDone <- true
@ -125,14 +128,15 @@ func TestTraceSymbolize(t *testing.T) {
wp.Write(data[:]) wp.Write(data[:])
<-pipeReadDone <-pipeReadDone
oldGoMaxProcs := runtime.GOMAXPROCS(1)
Stop() Stop()
runtime.GOMAXPROCS(oldGoMaxProcs)
events, _ := parseTrace(t, buf) events, _ := parseTrace(t, buf)
// Now check that the stacks are correct. // Now check that the stacks are correct.
type frame struct {
Fn string
Line int
}
type eventDesc struct { type eventDesc struct {
Type byte Type byte
Stk []frame Stk []frame
@ -140,90 +144,96 @@ func TestTraceSymbolize(t *testing.T) {
want := []eventDesc{ want := []eventDesc{
{trace.EvGCStart, []frame{ {trace.EvGCStart, []frame{
{"runtime.GC", 0}, {"runtime.GC", 0},
{"runtime/trace_test.TestTraceSymbolize", 107}, {"runtime/trace_test.TestTraceSymbolize", 0},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
{trace.EvGoStart, []frame{ {trace.EvGoStart, []frame{
{"runtime/trace_test.TestTraceSymbolize.func1", 37}, {"runtime/trace_test.TestTraceSymbolize.func1", 0},
}}, }},
{trace.EvGoSched, []frame{ {trace.EvGoSched, []frame{
{"runtime/trace_test.TestTraceSymbolize", 108}, {"runtime/trace_test.TestTraceSymbolize", 111},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
{trace.EvGoCreate, []frame{ {trace.EvGoCreate, []frame{
{"runtime/trace_test.TestTraceSymbolize", 37}, {"runtime/trace_test.TestTraceSymbolize", 40},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
{trace.EvGoStop, []frame{ {trace.EvGoStop, []frame{
{"runtime.block", 0}, {"runtime.block", 0},
{"runtime/trace_test.TestTraceSymbolize.func1", 38}, {"runtime/trace_test.TestTraceSymbolize.func1", 0},
}}, }},
{trace.EvGoStop, []frame{ {trace.EvGoStop, []frame{
{"runtime.chansend1", 0}, {"runtime.chansend1", 0},
{"runtime/trace_test.TestTraceSymbolize.func2", 42}, {"runtime/trace_test.TestTraceSymbolize.func2", 0},
}}, }},
{trace.EvGoStop, []frame{ {trace.EvGoStop, []frame{
{"runtime.chanrecv1", 0}, {"runtime.chanrecv1", 0},
{"runtime/trace_test.TestTraceSymbolize.func3", 46}, {"runtime/trace_test.TestTraceSymbolize.func3", 0},
}}, }},
{trace.EvGoBlockRecv, []frame{ {trace.EvGoBlockRecv, []frame{
{"runtime.chanrecv1", 0}, {"runtime.chanrecv1", 0},
{"runtime/trace_test.TestTraceSymbolize.func4", 50}, {"runtime/trace_test.TestTraceSymbolize.func4", 0},
}}, }},
{trace.EvGoUnblock, []frame{ {trace.EvGoUnblock, []frame{
{"runtime.chansend1", 0}, {"runtime.chansend1", 0},
{"runtime/trace_test.TestTraceSymbolize", 110}, {"runtime/trace_test.TestTraceSymbolize", 113},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
{trace.EvGoBlockSend, []frame{ {trace.EvGoBlockSend, []frame{
{"runtime.chansend1", 0}, {"runtime.chansend1", 0},
{"runtime/trace_test.TestTraceSymbolize.func5", 54}, {"runtime/trace_test.TestTraceSymbolize.func5", 0},
}}, }},
{trace.EvGoUnblock, []frame{ {trace.EvGoUnblock, []frame{
{"runtime.chanrecv1", 0}, {"runtime.chanrecv1", 0},
{"runtime/trace_test.TestTraceSymbolize", 111}, {"runtime/trace_test.TestTraceSymbolize", 114},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
{trace.EvGoBlockSelect, []frame{ {trace.EvGoBlockSelect, []frame{
{"runtime.selectgo", 0}, {"runtime.selectgo", 0},
{"runtime/trace_test.TestTraceSymbolize.func6", 59}, {"runtime/trace_test.TestTraceSymbolize.func6", 0},
}}, }},
{trace.EvGoUnblock, []frame{ {trace.EvGoUnblock, []frame{
{"runtime.selectgo", 0}, {"runtime.selectgo", 0},
{"runtime/trace_test.TestTraceSymbolize", 112}, {"runtime/trace_test.TestTraceSymbolize", 115},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
{trace.EvGoBlockSync, []frame{ {trace.EvGoBlockSync, []frame{
{"sync.(*Mutex).Lock", 0}, {"sync.(*Mutex).Lock", 0},
{"runtime/trace_test.TestTraceSymbolize.func7", 67}, {"runtime/trace_test.TestTraceSymbolize.func7", 0},
}}, }},
{trace.EvGoUnblock, []frame{ {trace.EvGoUnblock, []frame{
{"sync.(*Mutex).Unlock", 0}, {"sync.(*Mutex).Unlock", 0},
{"runtime/trace_test.TestTraceSymbolize", 116}, {"runtime/trace_test.TestTraceSymbolize", 0},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
{trace.EvGoBlockSync, []frame{ {trace.EvGoBlockSync, []frame{
{"sync.(*WaitGroup).Wait", 0}, {"sync.(*WaitGroup).Wait", 0},
{"runtime/trace_test.TestTraceSymbolize.func8", 73}, {"runtime/trace_test.TestTraceSymbolize.func8", 0},
}}, }},
{trace.EvGoUnblock, []frame{ {trace.EvGoUnblock, []frame{
{"sync.(*WaitGroup).Add", 0}, {"sync.(*WaitGroup).Add", 0},
{"sync.(*WaitGroup).Done", 0}, {"sync.(*WaitGroup).Done", 0},
{"runtime/trace_test.TestTraceSymbolize", 117}, {"runtime/trace_test.TestTraceSymbolize", 120},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
{trace.EvGoBlockCond, []frame{ {trace.EvGoBlockCond, []frame{
{"sync.(*Cond).Wait", 0}, {"sync.(*Cond).Wait", 0},
{"runtime/trace_test.TestTraceSymbolize.func9", 78}, {"runtime/trace_test.TestTraceSymbolize.func9", 0},
}}, }},
{trace.EvGoUnblock, []frame{ {trace.EvGoUnblock, []frame{
{"sync.(*Cond).Signal", 0}, {"sync.(*Cond).Signal", 0},
{"runtime/trace_test.TestTraceSymbolize", 118}, {"runtime/trace_test.TestTraceSymbolize", 0},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
{trace.EvGoSleep, []frame{ {trace.EvGoSleep, []frame{
{"time.Sleep", 0}, {"time.Sleep", 0},
{"runtime/trace_test.TestTraceSymbolize", 109}, {"runtime/trace_test.TestTraceSymbolize", 0},
{"testing.tRunner", 0},
}},
{trace.EvGomaxprocs, []frame{
{"runtime.startTheWorld", 0}, // this is when the current gomaxprocs is logged.
{"runtime.GOMAXPROCS", 0},
{"runtime/trace_test.TestTraceSymbolize", 0},
{"testing.tRunner", 0}, {"testing.tRunner", 0},
}}, }},
} }
@ -235,7 +245,7 @@ func TestTraceSymbolize(t *testing.T) {
{"net.(*netFD).accept", 0}, {"net.(*netFD).accept", 0},
{"net.(*TCPListener).accept", 0}, {"net.(*TCPListener).accept", 0},
{"net.(*TCPListener).Accept", 0}, {"net.(*TCPListener).Accept", 0},
{"runtime/trace_test.TestTraceSymbolize.func10", 86}, {"runtime/trace_test.TestTraceSymbolize.func10", 0},
}}, }},
{trace.EvGoSysCall, []frame{ {trace.EvGoSysCall, []frame{
{"syscall.read", 0}, {"syscall.read", 0},
@ -243,7 +253,7 @@ func TestTraceSymbolize(t *testing.T) {
{"internal/poll.(*FD).Read", 0}, {"internal/poll.(*FD).Read", 0},
{"os.(*File).read", 0}, {"os.(*File).read", 0},
{"os.(*File).Read", 0}, {"os.(*File).Read", 0},
{"runtime/trace_test.TestTraceSymbolize.func11", 102}, {"runtime/trace_test.TestTraceSymbolize.func11", 0},
}}, }},
}...) }...)
} }
@ -264,22 +274,50 @@ func TestTraceSymbolize(t *testing.T) {
matched[i] = true matched[i] = true
} }
} }
for i, m := range matched { for i, w := range want {
if m { if matched[i] {
continue continue
} }
w := want[i] seen, n := dumpEventStacks(w.Type, events)
t.Errorf("did not match event %v at %v:%v", trace.EventDescriptions[w.Type].Name, w.Stk[0].Fn, w.Stk[0].Line) t.Errorf("Did not match event %v with stack\n%s\nSeen %d events of the type\n%s",
t.Errorf("seen the following events of this type:") trace.EventDescriptions[w.Type].Name, dumpFrames(w.Stk), n, seen)
for _, ev := range events {
if ev.Type != w.Type {
continue
}
for _, f := range ev.Stk {
t.Logf(" %v :: %s:%v", f.Fn, f.File, f.Line)
}
t.Logf("---")
}
t.Logf("======")
} }
} }
func dumpEventStacks(typ byte, events []*trace.Event) ([]byte, int) {
matched := 0
o := new(bytes.Buffer)
tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
for _, ev := range events {
if ev.Type != typ {
continue
}
matched++
fmt.Fprintf(tw, "Offset %d\n", ev.Off)
for _, f := range ev.Stk {
fname := f.File
if idx := strings.Index(fname, "/go/src/"); idx > 0 {
fname = fname[idx:]
}
fmt.Fprintf(tw, " %v\t%s:%d\n", f.Fn, fname, f.Line)
}
}
tw.Flush()
return o.Bytes(), matched
}
type frame struct {
Fn string
Line int
}
func dumpFrames(frames []frame) []byte {
o := new(bytes.Buffer)
tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
for _, f := range frames {
fmt.Fprintf(tw, " %v\t :%d\n", f.Fn, f.Line)
}
tw.Flush()
return o.Bytes()
}