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