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.
23 // TestTraceSymbolize tests symbolization and that events has proper stacks.
24 // In particular that we strip bottom uninteresting frames like goexit,
25 // top uninteresting frames (runtime guts).
26 func TestTraceSymbolize(t *testing.T) {
27 skipTraceSymbolizeTestIfNecessary(t)
29 buf := new(bytes.Buffer)
30 if err := Start(buf); err != nil {
31 t.Fatalf("failed to start tracing: %v", err)
33 defer Stop() // in case of early return
35 // Now we will do a bunch of things for which we verify stacks later.
36 // It is impossible to ensure that a goroutine has actually blocked
37 // on a channel, in a select or otherwise. So we kick off goroutines
38 // that need to block first in the hope that while we are executing
39 // the rest of the test, they will block.
51 done1 := make(chan bool)
55 done2 := make(chan bool)
78 cv := sync.NewCond(&sync.Mutex{})
84 ln, err := net.Listen("tcp", "127.0.0.1:0")
86 t.Fatalf("failed to listen: %v", err)
91 t.Errorf("failed to accept: %v", err)
96 rp, wp, err := os.Pipe()
98 t.Fatalf("failed to create a pipe: %v", err)
102 pipeReadDone := make(chan bool)
103 go func() { // func11
109 time.Sleep(100 * time.Millisecond)
112 time.Sleep(100 * time.Millisecond) // the last chance for the goroutines above to block
122 c, err := net.Dial("tcp", ln.Addr().String())
124 t.Fatalf("failed to dial: %v", err)
131 oldGoMaxProcs := runtime.GOMAXPROCS(0)
132 runtime.GOMAXPROCS(oldGoMaxProcs + 1)
136 runtime.GOMAXPROCS(oldGoMaxProcs)
138 events, _ := parseTrace(t, buf)
140 // Now check that the stacks are correct.
141 type eventDesc struct {
146 {trace.EvGCStart, []frame{
148 {"runtime/trace_test.TestTraceSymbolize", 0},
149 {"testing.tRunner", 0},
151 {trace.EvGoStart, []frame{
152 {"runtime/trace_test.TestTraceSymbolize.func1", 0},
154 {trace.EvGoSched, []frame{
155 {"runtime/trace_test.TestTraceSymbolize", 111},
156 {"testing.tRunner", 0},
158 {trace.EvGoCreate, []frame{
159 {"runtime/trace_test.TestTraceSymbolize", 40},
160 {"testing.tRunner", 0},
162 {trace.EvGoStop, []frame{
163 {"runtime.block", 0},
164 {"runtime/trace_test.TestTraceSymbolize.func1", 0},
166 {trace.EvGoStop, []frame{
167 {"runtime.chansend1", 0},
168 {"runtime/trace_test.TestTraceSymbolize.func2", 0},
170 {trace.EvGoStop, []frame{
171 {"runtime.chanrecv1", 0},
172 {"runtime/trace_test.TestTraceSymbolize.func3", 0},
174 {trace.EvGoBlockRecv, []frame{
175 {"runtime.chanrecv1", 0},
176 {"runtime/trace_test.TestTraceSymbolize.func4", 0},
178 {trace.EvGoUnblock, []frame{
179 {"runtime.chansend1", 0},
180 {"runtime/trace_test.TestTraceSymbolize", 113},
181 {"testing.tRunner", 0},
183 {trace.EvGoBlockSend, []frame{
184 {"runtime.chansend1", 0},
185 {"runtime/trace_test.TestTraceSymbolize.func5", 0},
187 {trace.EvGoUnblock, []frame{
188 {"runtime.chanrecv1", 0},
189 {"runtime/trace_test.TestTraceSymbolize", 114},
190 {"testing.tRunner", 0},
192 {trace.EvGoBlockSelect, []frame{
193 {"runtime.selectgo", 0},
194 {"runtime/trace_test.TestTraceSymbolize.func6", 0},
196 {trace.EvGoUnblock, []frame{
197 {"runtime.selectgo", 0},
198 {"runtime/trace_test.TestTraceSymbolize", 115},
199 {"testing.tRunner", 0},
201 {trace.EvGoBlockSync, []frame{
202 {"sync.(*Mutex).Lock", 0},
203 {"runtime/trace_test.TestTraceSymbolize.func7", 0},
205 {trace.EvGoUnblock, []frame{
206 {"sync.(*Mutex).Unlock", 0},
207 {"runtime/trace_test.TestTraceSymbolize", 0},
208 {"testing.tRunner", 0},
210 {trace.EvGoBlockSync, []frame{
211 {"sync.(*WaitGroup).Wait", 0},
212 {"runtime/trace_test.TestTraceSymbolize.func8", 0},
214 {trace.EvGoUnblock, []frame{
215 {"sync.(*WaitGroup).Add", 0},
216 {"sync.(*WaitGroup).Done", 0},
217 {"runtime/trace_test.TestTraceSymbolize", 120},
218 {"testing.tRunner", 0},
220 {trace.EvGoBlockCond, []frame{
221 {"sync.(*Cond).Wait", 0},
222 {"runtime/trace_test.TestTraceSymbolize.func9", 0},
224 {trace.EvGoUnblock, []frame{
225 {"sync.(*Cond).Signal", 0},
226 {"runtime/trace_test.TestTraceSymbolize", 0},
227 {"testing.tRunner", 0},
229 {trace.EvGoSleep, []frame{
231 {"runtime/trace_test.TestTraceSymbolize", 0},
232 {"testing.tRunner", 0},
234 {trace.EvGomaxprocs, []frame{
235 {"runtime.startTheWorld", 0}, // this is when the current gomaxprocs is logged.
236 {"runtime.startTheWorldGC", 0},
237 {"runtime.GOMAXPROCS", 0},
238 {"runtime/trace_test.TestTraceSymbolize", 0},
239 {"testing.tRunner", 0},
242 // Stacks for the following events are OS-dependent due to OS-specific code in net package.
243 if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
244 want = append(want, []eventDesc{
245 {trace.EvGoBlockNet, []frame{
246 {"internal/poll.(*FD).Accept", 0},
247 {"net.(*netFD).accept", 0},
248 {"net.(*TCPListener).accept", 0},
249 {"net.(*TCPListener).Accept", 0},
250 {"runtime/trace_test.TestTraceSymbolize.func10", 0},
252 {trace.EvGoSysCall, []frame{
255 {"internal/poll.(*FD).Read", 0},
256 {"os.(*File).read", 0},
257 {"os.(*File).Read", 0},
258 {"runtime/trace_test.TestTraceSymbolize.func11", 0},
262 matched := make([]bool, len(want))
263 for _, ev := range events {
265 for i, w := range want {
266 if matched[i] || w.Type != ev.Type || len(w.Stk) != len(ev.Stk) {
270 for fi, f := range ev.Stk {
272 if wf.Fn != f.Fn || wf.Line != 0 && wf.Line != f.Line {
279 for i, w := range want {
283 seen, n := dumpEventStacks(w.Type, events)
284 t.Errorf("Did not match event %v with stack\n%s\nSeen %d events of the type\n%s",
285 trace.EventDescriptions[w.Type].Name, dumpFrames(w.Stk), n, seen)
289 func skipTraceSymbolizeTestIfNecessary(t *testing.T) {
290 testenv.MustHaveGoBuild(t)
292 t.Skip("skipping because -test.trace is set")
296 func dumpEventStacks(typ byte, events []*trace.Event) ([]byte, int) {
298 o := new(bytes.Buffer)
299 tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
300 for _, ev := range events {
305 fmt.Fprintf(tw, "Offset %d\n", ev.Off)
306 for _, f := range ev.Stk {
308 if idx := strings.Index(fname, "/go/src/"); idx > 0 {
311 fmt.Fprintf(tw, " %v\t%s:%d\n", f.Fn, fname, f.Line)
315 return o.Bytes(), matched
323 func dumpFrames(frames []frame) []byte {
324 o := new(bytes.Buffer)
325 tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
327 for _, f := range frames {
328 fmt.Fprintf(tw, " %v\t :%d\n", f.Fn, f.Line)