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.
10 "internal/goexperiment"
24 // TestTraceSymbolize tests symbolization and that events has proper stacks.
25 // In particular that we strip bottom uninteresting frames like goexit,
26 // top uninteresting frames (runtime guts).
27 func TestTraceSymbolize(t *testing.T) {
28 skipTraceSymbolizeTestIfNecessary(t)
30 buf := new(bytes.Buffer)
31 if err := Start(buf); err != nil {
32 t.Fatalf("failed to start tracing: %v", err)
34 defer Stop() // in case of early return
36 // Now we will do a bunch of things for which we verify stacks later.
37 // It is impossible to ensure that a goroutine has actually blocked
38 // on a channel, in a select or otherwise. So we kick off goroutines
39 // that need to block first in the hope that while we are executing
40 // the rest of the test, they will block.
52 done1 := make(chan bool)
56 done2 := make(chan bool)
79 cv := sync.NewCond(&sync.Mutex{})
85 ln, err := net.Listen("tcp", "127.0.0.1:0")
87 t.Fatalf("failed to listen: %v", err)
92 t.Errorf("failed to accept: %v", err)
97 rp, wp, err := os.Pipe()
99 t.Fatalf("failed to create a pipe: %v", err)
103 pipeReadDone := make(chan bool)
104 go func() { // func11
110 time.Sleep(100 * time.Millisecond)
113 time.Sleep(100 * time.Millisecond) // the last chance for the goroutines above to block
123 c, err := net.Dial("tcp", ln.Addr().String())
125 t.Fatalf("failed to dial: %v", err)
132 oldGoMaxProcs := runtime.GOMAXPROCS(0)
133 runtime.GOMAXPROCS(oldGoMaxProcs + 1)
137 runtime.GOMAXPROCS(oldGoMaxProcs)
139 events, _ := parseTrace(t, buf)
141 // Now check that the stacks are correct.
142 type eventDesc struct {
147 {trace.EvGCStart, []frame{
149 {"runtime/trace_test.TestTraceSymbolize", 0},
150 {"testing.tRunner", 0},
152 {trace.EvGoStart, []frame{
153 {"runtime/trace_test.TestTraceSymbolize.func1", 0},
155 {trace.EvGoSched, []frame{
156 {"runtime/trace_test.TestTraceSymbolize", 112},
157 {"testing.tRunner", 0},
159 {trace.EvGoCreate, []frame{
160 {"runtime/trace_test.TestTraceSymbolize", 41},
161 {"testing.tRunner", 0},
163 {trace.EvGoStop, []frame{
164 {"runtime.block", 0},
165 {"runtime/trace_test.TestTraceSymbolize.func1", 0},
167 {trace.EvGoStop, []frame{
168 {"runtime.chansend1", 0},
169 {"runtime/trace_test.TestTraceSymbolize.func2", 0},
171 {trace.EvGoStop, []frame{
172 {"runtime.chanrecv1", 0},
173 {"runtime/trace_test.TestTraceSymbolize.func3", 0},
175 {trace.EvGoBlockRecv, []frame{
176 {"runtime.chanrecv1", 0},
177 {"runtime/trace_test.TestTraceSymbolize.func4", 0},
179 {trace.EvGoUnblock, []frame{
180 {"runtime.chansend1", 0},
181 {"runtime/trace_test.TestTraceSymbolize", 114},
182 {"testing.tRunner", 0},
184 {trace.EvGoBlockSend, []frame{
185 {"runtime.chansend1", 0},
186 {"runtime/trace_test.TestTraceSymbolize.func5", 0},
188 {trace.EvGoUnblock, []frame{
189 {"runtime.chanrecv1", 0},
190 {"runtime/trace_test.TestTraceSymbolize", 115},
191 {"testing.tRunner", 0},
193 {trace.EvGoBlockSelect, []frame{
194 {"runtime.selectgo", 0},
195 {"runtime/trace_test.TestTraceSymbolize.func6", 0},
197 {trace.EvGoUnblock, []frame{
198 {"runtime.selectgo", 0},
199 {"runtime/trace_test.TestTraceSymbolize", 116},
200 {"testing.tRunner", 0},
202 {trace.EvGoBlockSync, []frame{
203 {"sync.(*Mutex).Lock", 0},
204 {"runtime/trace_test.TestTraceSymbolize.func7", 0},
206 {trace.EvGoUnblock, []frame{
207 {"sync.(*Mutex).Unlock", 0},
208 {"runtime/trace_test.TestTraceSymbolize", 0},
209 {"testing.tRunner", 0},
211 {trace.EvGoBlockSync, []frame{
212 {"sync.(*WaitGroup).Wait", 0},
213 {"runtime/trace_test.TestTraceSymbolize.func8", 0},
215 {trace.EvGoUnblock, []frame{
216 {"sync.(*WaitGroup).Add", 0},
217 {"sync.(*WaitGroup).Done", 0},
218 {"runtime/trace_test.TestTraceSymbolize", 121},
219 {"testing.tRunner", 0},
221 {trace.EvGoBlockCond, []frame{
222 {"sync.(*Cond).Wait", 0},
223 {"runtime/trace_test.TestTraceSymbolize.func9", 0},
225 {trace.EvGoUnblock, []frame{
226 {"sync.(*Cond).Signal", 0},
227 {"runtime/trace_test.TestTraceSymbolize", 0},
228 {"testing.tRunner", 0},
230 {trace.EvGoSleep, []frame{
232 {"runtime/trace_test.TestTraceSymbolize", 0},
233 {"testing.tRunner", 0},
235 {trace.EvGomaxprocs, []frame{
236 {"runtime.startTheWorld", 0}, // this is when the current gomaxprocs is logged.
237 {"runtime.startTheWorldGC", 0},
238 {"runtime.GOMAXPROCS", 0},
239 {"runtime/trace_test.TestTraceSymbolize", 0},
240 {"testing.tRunner", 0},
243 // Stacks for the following events are OS-dependent due to OS-specific code in net package.
244 if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
245 want = append(want, []eventDesc{
246 {trace.EvGoBlockNet, []frame{
247 {"internal/poll.(*FD).Accept", 0},
248 {"net.(*netFD).accept", 0},
249 {"net.(*TCPListener).accept", 0},
250 {"net.(*TCPListener).Accept", 0},
251 {"runtime/trace_test.TestTraceSymbolize.func10", 0},
253 {trace.EvGoSysCall, []frame{
256 {"internal/poll.ignoringEINTRIO", 0},
257 {"internal/poll.(*FD).Read", 0},
258 {"os.(*File).read", 0},
259 {"os.(*File).Read", 0},
260 {"runtime/trace_test.TestTraceSymbolize.func11", 0},
264 matched := make([]bool, len(want))
265 for _, ev := range events {
267 for i, w := range want {
268 if matched[i] || w.Type != ev.Type || len(w.Stk) != len(ev.Stk) {
272 for fi, f := range ev.Stk {
274 if wf.Fn != f.Fn || wf.Line != 0 && wf.Line != f.Line {
281 for i, w := range want {
285 seen, n := dumpEventStacks(w.Type, events)
286 t.Errorf("Did not match event %v with stack\n%s\nSeen %d events of the type\n%s",
287 trace.EventDescriptions[w.Type].Name, dumpFrames(w.Stk), n, seen)
291 func skipTraceSymbolizeTestIfNecessary(t *testing.T) {
292 testenv.MustHaveGoBuild(t)
293 if goexperiment.ExecTracer2 {
294 // An equivalent test exists in internal/trace/v2.
295 t.Skip("skipping because this test is incompatible with the new tracer")
298 t.Skip("skipping because -test.trace is set")
302 func dumpEventStacks(typ byte, events []*trace.Event) ([]byte, int) {
304 o := new(bytes.Buffer)
305 tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
306 for _, ev := range events {
311 fmt.Fprintf(tw, "Offset %d\n", ev.Off)
312 for _, f := range ev.Stk {
314 if idx := strings.Index(fname, "/go/src/"); idx > 0 {
317 fmt.Fprintf(tw, " %v\t%s:%d\n", f.Fn, fname, f.Line)
321 return o.Bytes(), matched
329 func dumpFrames(frames []frame) []byte {
330 o := new(bytes.Buffer)
331 tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
333 for _, f := range frames {
334 fmt.Fprintf(tw, " %v\t :%d\n", f.Fn, f.Line)