github.com/bir3/gocompiler@v0.9.2202/src/internal/trace/goroutines.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 6 7 import ( 8 "sort" 9 "strings" 10 ) 11 12 // GDesc contains statistics and execution details of a single goroutine. 13 type GDesc struct { 14 ID uint64 15 Name string 16 PC uint64 17 CreationTime int64 18 StartTime int64 19 EndTime int64 20 21 // List of regions in the goroutine, sorted based on the start time. 22 Regions []*UserRegionDesc 23 24 // Statistics of execution time during the goroutine execution. 25 GExecutionStat 26 27 *gdesc // private part. 28 } 29 30 // UserRegionDesc represents a region and goroutine execution stats 31 // while the region was active. 32 type UserRegionDesc struct { 33 TaskID uint64 34 Name string 35 36 // Region start event. Normally EvUserRegion start event or nil, 37 // but can be EvGoCreate event if the region is a synthetic 38 // region representing task inheritance from the parent goroutine. 39 Start *Event 40 41 // Region end event. Normally EvUserRegion end event or nil, 42 // but can be EvGoStop or EvGoEnd event if the goroutine 43 // terminated without explicitly ending the region. 44 End *Event 45 46 GExecutionStat 47 } 48 49 // GExecutionStat contains statistics about a goroutine's execution 50 // during a period of time. 51 type GExecutionStat struct { 52 ExecTime int64 53 SchedWaitTime int64 54 IOTime int64 55 BlockTime int64 56 SyscallTime int64 57 GCTime int64 58 SweepTime int64 59 TotalTime int64 60 } 61 62 // sub returns the stats v-s. 63 func (s GExecutionStat) sub(v GExecutionStat) (r GExecutionStat) { 64 r = s 65 r.ExecTime -= v.ExecTime 66 r.SchedWaitTime -= v.SchedWaitTime 67 r.IOTime -= v.IOTime 68 r.BlockTime -= v.BlockTime 69 r.SyscallTime -= v.SyscallTime 70 r.GCTime -= v.GCTime 71 r.SweepTime -= v.SweepTime 72 r.TotalTime -= v.TotalTime 73 return r 74 } 75 76 // snapshotStat returns the snapshot of the goroutine execution statistics. 77 // This is called as we process the ordered trace event stream. lastTs and 78 // activeGCStartTime are used to process pending statistics if this is called 79 // before any goroutine end event. 80 func (g *GDesc) snapshotStat(lastTs, activeGCStartTime int64) (ret GExecutionStat) { 81 ret = g.GExecutionStat 82 83 if g.gdesc == nil { 84 return ret // finalized GDesc. No pending state. 85 } 86 87 if activeGCStartTime != 0 { // terminating while GC is active 88 if g.CreationTime < activeGCStartTime { 89 ret.GCTime += lastTs - activeGCStartTime 90 } else { 91 // The goroutine's lifetime completely overlaps 92 // with a GC. 93 ret.GCTime += lastTs - g.CreationTime 94 } 95 } 96 97 if g.TotalTime == 0 { 98 ret.TotalTime = lastTs - g.CreationTime 99 } 100 101 if g.lastStartTime != 0 { 102 ret.ExecTime += lastTs - g.lastStartTime 103 } 104 if g.blockNetTime != 0 { 105 ret.IOTime += lastTs - g.blockNetTime 106 } 107 if g.blockSyncTime != 0 { 108 ret.BlockTime += lastTs - g.blockSyncTime 109 } 110 if g.blockSyscallTime != 0 { 111 ret.SyscallTime += lastTs - g.blockSyscallTime 112 } 113 if g.blockSchedTime != 0 { 114 ret.SchedWaitTime += lastTs - g.blockSchedTime 115 } 116 if g.blockSweepTime != 0 { 117 ret.SweepTime += lastTs - g.blockSweepTime 118 } 119 return ret 120 } 121 122 // finalize is called when processing a goroutine end event or at 123 // the end of trace processing. This finalizes the execution stat 124 // and any active regions in the goroutine, in which case trigger is nil. 125 func (g *GDesc) finalize(lastTs, activeGCStartTime int64, trigger *Event) { 126 if trigger != nil { 127 g.EndTime = trigger.Ts 128 } 129 finalStat := g.snapshotStat(lastTs, activeGCStartTime) 130 131 g.GExecutionStat = finalStat 132 133 // System goroutines are never part of regions, even though they 134 // "inherit" a task due to creation (EvGoCreate) from within a region. 135 // This may happen e.g. if the first GC is triggered within a region, 136 // starting the GC worker goroutines. 137 if !IsSystemGoroutine(g.Name) { 138 for _, s := range g.activeRegions { 139 s.End = trigger 140 s.GExecutionStat = finalStat.sub(s.GExecutionStat) 141 g.Regions = append(g.Regions, s) 142 } 143 } 144 *(g.gdesc) = gdesc{} 145 } 146 147 // gdesc is a private part of GDesc that is required only during analysis. 148 type gdesc struct { 149 lastStartTime int64 150 blockNetTime int64 151 blockSyncTime int64 152 blockSyscallTime int64 153 blockSweepTime int64 154 blockGCTime int64 155 blockSchedTime int64 156 157 activeRegions []*UserRegionDesc // stack of active regions 158 } 159 160 // GoroutineStats generates statistics for all goroutines in the trace. 161 func GoroutineStats(events []*Event) map[uint64]*GDesc { 162 gs := make(map[uint64]*GDesc) 163 var lastTs int64 164 var gcStartTime int64 // gcStartTime == 0 indicates gc is inactive. 165 for _, ev := range events { 166 lastTs = ev.Ts 167 switch ev.Type { 168 case EvGoCreate: 169 g := &GDesc{ID: ev.Args[0], CreationTime: ev.Ts, gdesc: new(gdesc)} 170 g.blockSchedTime = ev.Ts 171 // When a goroutine is newly created, inherit the task 172 // of the active region. For ease handling of this 173 // case, we create a fake region description with the 174 // task id. This isn't strictly necessary as this 175 // goroutine may not be associated with the task, but 176 // it can be convenient to see all children created 177 // during a region. 178 if creatorG := gs[ev.G]; creatorG != nil && len(creatorG.gdesc.activeRegions) > 0 { 179 regions := creatorG.gdesc.activeRegions 180 s := regions[len(regions)-1] 181 if s.TaskID != 0 { 182 g.gdesc.activeRegions = []*UserRegionDesc{ 183 {TaskID: s.TaskID, Start: ev}, 184 } 185 } 186 } 187 gs[g.ID] = g 188 case EvGoStart, EvGoStartLabel: 189 g := gs[ev.G] 190 if g.PC == 0 && len(ev.Stk) > 0 { 191 g.PC = ev.Stk[0].PC 192 g.Name = ev.Stk[0].Fn 193 } 194 g.lastStartTime = ev.Ts 195 if g.StartTime == 0 { 196 g.StartTime = ev.Ts 197 } 198 if g.blockSchedTime != 0 { 199 g.SchedWaitTime += ev.Ts - g.blockSchedTime 200 g.blockSchedTime = 0 201 } 202 case EvGoEnd, EvGoStop: 203 g := gs[ev.G] 204 g.finalize(ev.Ts, gcStartTime, ev) 205 case EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect, 206 EvGoBlockSync, EvGoBlockCond: 207 g := gs[ev.G] 208 g.ExecTime += ev.Ts - g.lastStartTime 209 g.lastStartTime = 0 210 g.blockSyncTime = ev.Ts 211 case EvGoSched, EvGoPreempt: 212 g := gs[ev.G] 213 g.ExecTime += ev.Ts - g.lastStartTime 214 g.lastStartTime = 0 215 g.blockSchedTime = ev.Ts 216 case EvGoSleep, EvGoBlock: 217 g := gs[ev.G] 218 g.ExecTime += ev.Ts - g.lastStartTime 219 g.lastStartTime = 0 220 case EvGoBlockNet: 221 g := gs[ev.G] 222 g.ExecTime += ev.Ts - g.lastStartTime 223 g.lastStartTime = 0 224 g.blockNetTime = ev.Ts 225 case EvGoBlockGC: 226 g := gs[ev.G] 227 g.ExecTime += ev.Ts - g.lastStartTime 228 g.lastStartTime = 0 229 g.blockGCTime = ev.Ts 230 case EvGoUnblock: 231 g := gs[ev.Args[0]] 232 if g.blockNetTime != 0 { 233 g.IOTime += ev.Ts - g.blockNetTime 234 g.blockNetTime = 0 235 } 236 if g.blockSyncTime != 0 { 237 g.BlockTime += ev.Ts - g.blockSyncTime 238 g.blockSyncTime = 0 239 } 240 g.blockSchedTime = ev.Ts 241 case EvGoSysBlock: 242 g := gs[ev.G] 243 g.ExecTime += ev.Ts - g.lastStartTime 244 g.lastStartTime = 0 245 g.blockSyscallTime = ev.Ts 246 case EvGoSysExit: 247 g := gs[ev.G] 248 if g.blockSyscallTime != 0 { 249 g.SyscallTime += ev.Ts - g.blockSyscallTime 250 g.blockSyscallTime = 0 251 } 252 g.blockSchedTime = ev.Ts 253 case EvGCSweepStart: 254 g := gs[ev.G] 255 if g != nil { 256 // Sweep can happen during GC on system goroutine. 257 g.blockSweepTime = ev.Ts 258 } 259 case EvGCSweepDone: 260 g := gs[ev.G] 261 if g != nil && g.blockSweepTime != 0 { 262 g.SweepTime += ev.Ts - g.blockSweepTime 263 g.blockSweepTime = 0 264 } 265 case EvGCStart: 266 gcStartTime = ev.Ts 267 case EvGCDone: 268 for _, g := range gs { 269 if g.EndTime != 0 { 270 continue 271 } 272 if gcStartTime < g.CreationTime { 273 g.GCTime += ev.Ts - g.CreationTime 274 } else { 275 g.GCTime += ev.Ts - gcStartTime 276 } 277 } 278 gcStartTime = 0 // indicates gc is inactive. 279 case EvUserRegion: 280 g := gs[ev.G] 281 switch mode := ev.Args[1]; mode { 282 case 0: // region start 283 g.activeRegions = append(g.activeRegions, &UserRegionDesc{ 284 Name: ev.SArgs[0], 285 TaskID: ev.Args[0], 286 Start: ev, 287 GExecutionStat: g.snapshotStat(lastTs, gcStartTime), 288 }) 289 case 1: // region end 290 var sd *UserRegionDesc 291 if regionStk := g.activeRegions; len(regionStk) > 0 { 292 n := len(regionStk) 293 sd = regionStk[n-1] 294 regionStk = regionStk[:n-1] // pop 295 g.activeRegions = regionStk 296 } else { 297 sd = &UserRegionDesc{ 298 Name: ev.SArgs[0], 299 TaskID: ev.Args[0], 300 } 301 } 302 sd.GExecutionStat = g.snapshotStat(lastTs, gcStartTime).sub(sd.GExecutionStat) 303 sd.End = ev 304 g.Regions = append(g.Regions, sd) 305 } 306 } 307 } 308 309 for _, g := range gs { 310 g.finalize(lastTs, gcStartTime, nil) 311 312 // sort based on region start time 313 sort.Slice(g.Regions, func(i, j int) bool { 314 x := g.Regions[i].Start 315 y := g.Regions[j].Start 316 if x == nil { 317 return true 318 } 319 if y == nil { 320 return false 321 } 322 return x.Ts < y.Ts 323 }) 324 325 g.gdesc = nil 326 } 327 328 return gs 329 } 330 331 // RelatedGoroutines finds a set of goroutines related to goroutine goid. 332 func RelatedGoroutines(events []*Event, goid uint64) map[uint64]bool { 333 // BFS of depth 2 over "unblock" edges 334 // (what goroutines unblock goroutine goid?). 335 gmap := make(map[uint64]bool) 336 gmap[goid] = true 337 for i := 0; i < 2; i++ { 338 gmap1 := make(map[uint64]bool) 339 for g := range gmap { 340 gmap1[g] = true 341 } 342 for _, ev := range events { 343 if ev.Type == EvGoUnblock && gmap[ev.Args[0]] { 344 gmap1[ev.G] = true 345 } 346 } 347 gmap = gmap1 348 } 349 gmap[0] = true // for GC events 350 return gmap 351 } 352 353 func IsSystemGoroutine(entryFn string) bool { 354 // This mimics runtime.isSystemGoroutine as closely as 355 // possible. 356 // Also, locked g in extra M (with empty entryFn) is system goroutine. 357 return entryFn == "" || entryFn != "runtime.main" && strings.HasPrefix(entryFn, "runtime.") 358 }