github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/runtime/trace/trace_stack_test.go (about) 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. 4 5 package trace_test 6 7 import ( 8 "bytes" 9 "internal/testenv" 10 "internal/trace" 11 "net" 12 "os" 13 "runtime" 14 . "runtime/trace" 15 "sync" 16 "testing" 17 "time" 18 ) 19 20 // TestTraceSymbolize tests symbolization and that events has proper stacks. 21 // In particular that we strip bottom uninteresting frames like goexit, 22 // top uninteresting frames (runtime guts). 23 func TestTraceSymbolize(t *testing.T) { 24 testenv.MustHaveGoBuild(t) 25 26 buf := new(bytes.Buffer) 27 if err := Start(buf); err != nil { 28 t.Fatalf("failed to start tracing: %v", err) 29 } 30 defer Stop() // in case of early return 31 32 // Now we will do a bunch of things for which we verify stacks later. 33 // It is impossible to ensure that a goroutine has actually blocked 34 // on a channel, in a select or otherwise. So we kick off goroutines 35 // that need to block first in the hope that while we are executing 36 // the rest of the test, they will block. 37 go func() { 38 select {} 39 }() 40 go func() { 41 var c chan int 42 c <- 0 43 }() 44 go func() { 45 var c chan int 46 <-c 47 }() 48 done1 := make(chan bool) 49 go func() { 50 <-done1 51 }() 52 done2 := make(chan bool) 53 go func() { 54 done2 <- true 55 }() 56 c1 := make(chan int) 57 c2 := make(chan int) 58 go func() { 59 select { 60 case <-c1: 61 case <-c2: 62 } 63 }() 64 var mu sync.Mutex 65 mu.Lock() 66 go func() { 67 mu.Lock() 68 mu.Unlock() 69 }() 70 var wg sync.WaitGroup 71 wg.Add(1) 72 go func() { 73 wg.Wait() 74 }() 75 cv := sync.NewCond(&sync.Mutex{}) 76 go func() { 77 cv.L.Lock() 78 cv.Wait() 79 cv.L.Unlock() 80 }() 81 ln, err := net.Listen("tcp", "127.0.0.1:0") 82 if err != nil { 83 t.Fatalf("failed to listen: %v", err) 84 } 85 go func() { 86 c, err := ln.Accept() 87 if err != nil { 88 t.Errorf("failed to accept: %v", err) 89 return 90 } 91 c.Close() 92 }() 93 rp, wp, err := os.Pipe() 94 if err != nil { 95 t.Fatalf("failed to create a pipe: %v", err) 96 } 97 defer rp.Close() 98 defer wp.Close() 99 pipeReadDone := make(chan bool) 100 go func() { 101 var data [1]byte 102 rp.Read(data[:]) 103 pipeReadDone <- true 104 }() 105 106 time.Sleep(100 * time.Millisecond) 107 runtime.GC() 108 runtime.Gosched() 109 time.Sleep(100 * time.Millisecond) // the last chance for the goroutines above to block 110 done1 <- true 111 <-done2 112 select { 113 case c1 <- 0: 114 case c2 <- 0: 115 } 116 mu.Unlock() 117 wg.Done() 118 cv.Signal() 119 c, err := net.Dial("tcp", ln.Addr().String()) 120 if err != nil { 121 t.Fatalf("failed to dial: %v", err) 122 } 123 c.Close() 124 var data [1]byte 125 wp.Write(data[:]) 126 <-pipeReadDone 127 128 Stop() 129 events, _ := parseTrace(t, buf) 130 131 // Now check that the stacks are correct. 132 type frame struct { 133 Fn string 134 Line int 135 } 136 type eventDesc struct { 137 Type byte 138 Stk []frame 139 } 140 want := []eventDesc{ 141 {trace.EvGCStart, []frame{ 142 {"runtime.GC", 0}, 143 {"runtime/trace_test.TestTraceSymbolize", 107}, 144 {"testing.tRunner", 0}, 145 }}, 146 {trace.EvGoStart, []frame{ 147 {"runtime/trace_test.TestTraceSymbolize.func1", 37}, 148 }}, 149 {trace.EvGoSched, []frame{ 150 {"runtime/trace_test.TestTraceSymbolize", 108}, 151 {"testing.tRunner", 0}, 152 }}, 153 {trace.EvGoCreate, []frame{ 154 {"runtime/trace_test.TestTraceSymbolize", 37}, 155 {"testing.tRunner", 0}, 156 }}, 157 {trace.EvGoStop, []frame{ 158 {"runtime.block", 0}, 159 {"runtime/trace_test.TestTraceSymbolize.func1", 38}, 160 }}, 161 {trace.EvGoStop, []frame{ 162 {"runtime.chansend1", 0}, 163 {"runtime/trace_test.TestTraceSymbolize.func2", 42}, 164 }}, 165 {trace.EvGoStop, []frame{ 166 {"runtime.chanrecv1", 0}, 167 {"runtime/trace_test.TestTraceSymbolize.func3", 46}, 168 }}, 169 {trace.EvGoBlockRecv, []frame{ 170 {"runtime.chanrecv1", 0}, 171 {"runtime/trace_test.TestTraceSymbolize.func4", 50}, 172 }}, 173 {trace.EvGoUnblock, []frame{ 174 {"runtime.chansend1", 0}, 175 {"runtime/trace_test.TestTraceSymbolize", 110}, 176 {"testing.tRunner", 0}, 177 }}, 178 {trace.EvGoBlockSend, []frame{ 179 {"runtime.chansend1", 0}, 180 {"runtime/trace_test.TestTraceSymbolize.func5", 54}, 181 }}, 182 {trace.EvGoUnblock, []frame{ 183 {"runtime.chanrecv1", 0}, 184 {"runtime/trace_test.TestTraceSymbolize", 111}, 185 {"testing.tRunner", 0}, 186 }}, 187 {trace.EvGoBlockSelect, []frame{ 188 {"runtime.selectgo", 0}, 189 {"runtime/trace_test.TestTraceSymbolize.func6", 59}, 190 }}, 191 {trace.EvGoUnblock, []frame{ 192 {"runtime.selectgo", 0}, 193 {"runtime/trace_test.TestTraceSymbolize", 112}, 194 {"testing.tRunner", 0}, 195 }}, 196 {trace.EvGoBlockSync, []frame{ 197 {"sync.(*Mutex).Lock", 0}, 198 {"runtime/trace_test.TestTraceSymbolize.func7", 67}, 199 }}, 200 {trace.EvGoUnblock, []frame{ 201 {"sync.(*Mutex).Unlock", 0}, 202 {"runtime/trace_test.TestTraceSymbolize", 116}, 203 {"testing.tRunner", 0}, 204 }}, 205 {trace.EvGoBlockSync, []frame{ 206 {"sync.(*WaitGroup).Wait", 0}, 207 {"runtime/trace_test.TestTraceSymbolize.func8", 73}, 208 }}, 209 {trace.EvGoUnblock, []frame{ 210 {"sync.(*WaitGroup).Add", 0}, 211 {"sync.(*WaitGroup).Done", 0}, 212 {"runtime/trace_test.TestTraceSymbolize", 117}, 213 {"testing.tRunner", 0}, 214 }}, 215 {trace.EvGoBlockCond, []frame{ 216 {"sync.(*Cond).Wait", 0}, 217 {"runtime/trace_test.TestTraceSymbolize.func9", 78}, 218 }}, 219 {trace.EvGoUnblock, []frame{ 220 {"sync.(*Cond).Signal", 0}, 221 {"runtime/trace_test.TestTraceSymbolize", 118}, 222 {"testing.tRunner", 0}, 223 }}, 224 {trace.EvGoSleep, []frame{ 225 {"time.Sleep", 0}, 226 {"runtime/trace_test.TestTraceSymbolize", 109}, 227 {"testing.tRunner", 0}, 228 }}, 229 } 230 // Stacks for the following events are OS-dependent due to OS-specific code in net package. 231 if runtime.GOOS != "windows" && runtime.GOOS != "plan9" { 232 want = append(want, []eventDesc{ 233 {trace.EvGoBlockNet, []frame{ 234 {"internal/poll.(*FD).Accept", 0}, 235 {"net.(*netFD).accept", 0}, 236 {"net.(*TCPListener).accept", 0}, 237 {"net.(*TCPListener).Accept", 0}, 238 {"runtime/trace_test.TestTraceSymbolize.func10", 86}, 239 }}, 240 {trace.EvGoSysCall, []frame{ 241 {"syscall.read", 0}, 242 {"syscall.Read", 0}, 243 {"internal/poll.(*FD).Read", 0}, 244 {"os.(*File).read", 0}, 245 {"os.(*File).Read", 0}, 246 {"runtime/trace_test.TestTraceSymbolize.func11", 102}, 247 }}, 248 }...) 249 } 250 matched := make([]bool, len(want)) 251 for _, ev := range events { 252 wantLoop: 253 for i, w := range want { 254 if matched[i] || w.Type != ev.Type || len(w.Stk) != len(ev.Stk) { 255 continue 256 } 257 258 for fi, f := range ev.Stk { 259 wf := w.Stk[fi] 260 if wf.Fn != f.Fn || wf.Line != 0 && wf.Line != f.Line { 261 continue wantLoop 262 } 263 } 264 matched[i] = true 265 } 266 } 267 for i, m := range matched { 268 if m { 269 continue 270 } 271 w := want[i] 272 t.Errorf("did not match event %v at %v:%v", trace.EventDescriptions[w.Type].Name, w.Stk[0].Fn, w.Stk[0].Line) 273 t.Errorf("seen the following events of this type:") 274 for _, ev := range events { 275 if ev.Type != w.Type { 276 continue 277 } 278 for _, f := range ev.Stk { 279 t.Logf(" %v :: %s:%v", f.Fn, f.File, f.Line) 280 } 281 t.Logf("---") 282 } 283 t.Logf("======") 284 } 285 }