Source file
src/runtime/trace/trace_stack_test.go
1
2
3
4
5 package trace_test
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/testenv"
11 "internal/trace"
12 "net"
13 "os"
14 "runtime"
15 . "runtime/trace"
16 "strings"
17 "sync"
18 "testing"
19 "text/tabwriter"
20 "time"
21 )
22
23
24
25
26 func TestTraceSymbolize(t *testing.T) {
27 skipTraceSymbolizeTestIfNecessary(t)
28
29 buf := new(bytes.Buffer)
30 if err := Start(buf); err != nil {
31 t.Fatalf("failed to start tracing: %v", err)
32 }
33 defer Stop()
34
35
36
37
38
39
40 go func() {
41 select {}
42 }()
43 go func() {
44 var c chan int
45 c <- 0
46 }()
47 go func() {
48 var c chan int
49 <-c
50 }()
51 done1 := make(chan bool)
52 go func() {
53 <-done1
54 }()
55 done2 := make(chan bool)
56 go func() {
57 done2 <- true
58 }()
59 c1 := make(chan int)
60 c2 := make(chan int)
61 go func() {
62 select {
63 case <-c1:
64 case <-c2:
65 }
66 }()
67 var mu sync.Mutex
68 mu.Lock()
69 go func() {
70 mu.Lock()
71 mu.Unlock()
72 }()
73 var wg sync.WaitGroup
74 wg.Add(1)
75 go func() {
76 wg.Wait()
77 }()
78 cv := sync.NewCond(&sync.Mutex{})
79 go func() {
80 cv.L.Lock()
81 cv.Wait()
82 cv.L.Unlock()
83 }()
84 ln, err := net.Listen("tcp", "127.0.0.1:0")
85 if err != nil {
86 t.Fatalf("failed to listen: %v", err)
87 }
88 go func() {
89 c, err := ln.Accept()
90 if err != nil {
91 t.Errorf("failed to accept: %v", err)
92 return
93 }
94 c.Close()
95 }()
96 rp, wp, err := os.Pipe()
97 if err != nil {
98 t.Fatalf("failed to create a pipe: %v", err)
99 }
100 defer rp.Close()
101 defer wp.Close()
102 pipeReadDone := make(chan bool)
103 go func() {
104 var data [1]byte
105 rp.Read(data[:])
106 pipeReadDone <- true
107 }()
108
109 time.Sleep(100 * time.Millisecond)
110 runtime.GC()
111 runtime.Gosched()
112 time.Sleep(100 * time.Millisecond)
113 done1 <- true
114 <-done2
115 select {
116 case c1 <- 0:
117 case c2 <- 0:
118 }
119 mu.Unlock()
120 wg.Done()
121 cv.Signal()
122 c, err := net.Dial("tcp", ln.Addr().String())
123 if err != nil {
124 t.Fatalf("failed to dial: %v", err)
125 }
126 c.Close()
127 var data [1]byte
128 wp.Write(data[:])
129 <-pipeReadDone
130
131 oldGoMaxProcs := runtime.GOMAXPROCS(0)
132 runtime.GOMAXPROCS(oldGoMaxProcs + 1)
133
134 Stop()
135
136 runtime.GOMAXPROCS(oldGoMaxProcs)
137
138 events, _ := parseTrace(t, buf)
139
140
141 type eventDesc struct {
142 Type byte
143 Stk []frame
144 }
145 want := []eventDesc{
146 {trace.EvGCStart, []frame{
147 {"runtime.GC", 0},
148 {"runtime/trace_test.TestTraceSymbolize", 0},
149 {"testing.tRunner", 0},
150 }},
151 {trace.EvGoStart, []frame{
152 {"runtime/trace_test.TestTraceSymbolize.func1", 0},
153 }},
154 {trace.EvGoSched, []frame{
155 {"runtime/trace_test.TestTraceSymbolize", 111},
156 {"testing.tRunner", 0},
157 }},
158 {trace.EvGoCreate, []frame{
159 {"runtime/trace_test.TestTraceSymbolize", 40},
160 {"testing.tRunner", 0},
161 }},
162 {trace.EvGoStop, []frame{
163 {"runtime.block", 0},
164 {"runtime/trace_test.TestTraceSymbolize.func1", 0},
165 }},
166 {trace.EvGoStop, []frame{
167 {"runtime.chansend1", 0},
168 {"runtime/trace_test.TestTraceSymbolize.func2", 0},
169 }},
170 {trace.EvGoStop, []frame{
171 {"runtime.chanrecv1", 0},
172 {"runtime/trace_test.TestTraceSymbolize.func3", 0},
173 }},
174 {trace.EvGoBlockRecv, []frame{
175 {"runtime.chanrecv1", 0},
176 {"runtime/trace_test.TestTraceSymbolize.func4", 0},
177 }},
178 {trace.EvGoUnblock, []frame{
179 {"runtime.chansend1", 0},
180 {"runtime/trace_test.TestTraceSymbolize", 113},
181 {"testing.tRunner", 0},
182 }},
183 {trace.EvGoBlockSend, []frame{
184 {"runtime.chansend1", 0},
185 {"runtime/trace_test.TestTraceSymbolize.func5", 0},
186 }},
187 {trace.EvGoUnblock, []frame{
188 {"runtime.chanrecv1", 0},
189 {"runtime/trace_test.TestTraceSymbolize", 114},
190 {"testing.tRunner", 0},
191 }},
192 {trace.EvGoBlockSelect, []frame{
193 {"runtime.selectgo", 0},
194 {"runtime/trace_test.TestTraceSymbolize.func6", 0},
195 }},
196 {trace.EvGoUnblock, []frame{
197 {"runtime.selectgo", 0},
198 {"runtime/trace_test.TestTraceSymbolize", 115},
199 {"testing.tRunner", 0},
200 }},
201 {trace.EvGoBlockSync, []frame{
202 {"sync.(*Mutex).Lock", 0},
203 {"runtime/trace_test.TestTraceSymbolize.func7", 0},
204 }},
205 {trace.EvGoUnblock, []frame{
206 {"sync.(*Mutex).Unlock", 0},
207 {"runtime/trace_test.TestTraceSymbolize", 0},
208 {"testing.tRunner", 0},
209 }},
210 {trace.EvGoBlockSync, []frame{
211 {"sync.(*WaitGroup).Wait", 0},
212 {"runtime/trace_test.TestTraceSymbolize.func8", 0},
213 }},
214 {trace.EvGoUnblock, []frame{
215 {"sync.(*WaitGroup).Add", 0},
216 {"sync.(*WaitGroup).Done", 0},
217 {"runtime/trace_test.TestTraceSymbolize", 120},
218 {"testing.tRunner", 0},
219 }},
220 {trace.EvGoBlockCond, []frame{
221 {"sync.(*Cond).Wait", 0},
222 {"runtime/trace_test.TestTraceSymbolize.func9", 0},
223 }},
224 {trace.EvGoUnblock, []frame{
225 {"sync.(*Cond).Signal", 0},
226 {"runtime/trace_test.TestTraceSymbolize", 0},
227 {"testing.tRunner", 0},
228 }},
229 {trace.EvGoSleep, []frame{
230 {"time.Sleep", 0},
231 {"runtime/trace_test.TestTraceSymbolize", 0},
232 {"testing.tRunner", 0},
233 }},
234 {trace.EvGomaxprocs, []frame{
235 {"runtime.startTheWorld", 0},
236 {"runtime.startTheWorldGC", 0},
237 {"runtime.GOMAXPROCS", 0},
238 {"runtime/trace_test.TestTraceSymbolize", 0},
239 {"testing.tRunner", 0},
240 }},
241 }
242
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},
251 }},
252 {trace.EvGoSysCall, []frame{
253 {"syscall.read", 0},
254 {"syscall.Read", 0},
255 {"internal/poll.ignoringEINTRIO", 0},
256 {"internal/poll.(*FD).Read", 0},
257 {"os.(*File).read", 0},
258 {"os.(*File).Read", 0},
259 {"runtime/trace_test.TestTraceSymbolize.func11", 0},
260 }},
261 }...)
262 }
263 matched := make([]bool, len(want))
264 for _, ev := range events {
265 wantLoop:
266 for i, w := range want {
267 if matched[i] || w.Type != ev.Type || len(w.Stk) != len(ev.Stk) {
268 continue
269 }
270
271 for fi, f := range ev.Stk {
272 wf := w.Stk[fi]
273 if wf.Fn != f.Fn || wf.Line != 0 && wf.Line != f.Line {
274 continue wantLoop
275 }
276 }
277 matched[i] = true
278 }
279 }
280 for i, w := range want {
281 if matched[i] {
282 continue
283 }
284 seen, n := dumpEventStacks(w.Type, events)
285 t.Errorf("Did not match event %v with stack\n%s\nSeen %d events of the type\n%s",
286 trace.EventDescriptions[w.Type].Name, dumpFrames(w.Stk), n, seen)
287 }
288 }
289
290 func skipTraceSymbolizeTestIfNecessary(t *testing.T) {
291 testenv.MustHaveGoBuild(t)
292 if IsEnabled() {
293 t.Skip("skipping because -test.trace is set")
294 }
295 }
296
297 func dumpEventStacks(typ byte, events []*trace.Event) ([]byte, int) {
298 matched := 0
299 o := new(bytes.Buffer)
300 tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
301 for _, ev := range events {
302 if ev.Type != typ {
303 continue
304 }
305 matched++
306 fmt.Fprintf(tw, "Offset %d\n", ev.Off)
307 for _, f := range ev.Stk {
308 fname := f.File
309 if idx := strings.Index(fname, "/go/src/"); idx > 0 {
310 fname = fname[idx:]
311 }
312 fmt.Fprintf(tw, " %v\t%s:%d\n", f.Fn, fname, f.Line)
313 }
314 }
315 tw.Flush()
316 return o.Bytes(), matched
317 }
318
319 type frame struct {
320 Fn string
321 Line int
322 }
323
324 func dumpFrames(frames []frame) []byte {
325 o := new(bytes.Buffer)
326 tw := tabwriter.NewWriter(o, 0, 8, 0, '\t', 0)
327
328 for _, f := range frames {
329 fmt.Fprintf(tw, " %v\t :%d\n", f.Fn, f.Line)
330 }
331 tw.Flush()
332 return o.Bytes()
333 }
334
View as plain text