github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/trace/v2/goroutinegen.go (about) 1 // Copyright 2023 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 tracev2 "github.com/go-asm/go/trace/v2" 9 ) 10 11 var _ generator = &goroutineGenerator{} 12 13 type goroutineGenerator struct { 14 globalRangeGenerator 15 globalMetricGenerator 16 stackSampleGenerator[tracev2.GoID] 17 logEventGenerator[tracev2.GoID] 18 19 gStates map[tracev2.GoID]*gState[tracev2.GoID] 20 focus tracev2.GoID 21 filter map[tracev2.GoID]struct{} 22 } 23 24 func newGoroutineGenerator(ctx *traceContext, focus tracev2.GoID, filter map[tracev2.GoID]struct{}) *goroutineGenerator { 25 gg := new(goroutineGenerator) 26 rg := func(ev *tracev2.Event) tracev2.GoID { 27 return ev.Goroutine() 28 } 29 gg.stackSampleGenerator.getResource = rg 30 gg.logEventGenerator.getResource = rg 31 gg.gStates = make(map[tracev2.GoID]*gState[tracev2.GoID]) 32 gg.focus = focus 33 gg.filter = filter 34 35 // Enable a filter on the emitter. 36 if filter != nil { 37 ctx.SetResourceFilter(func(resource uint64) bool { 38 _, ok := filter[tracev2.GoID(resource)] 39 return ok 40 }) 41 } 42 return gg 43 } 44 45 func (g *goroutineGenerator) Sync() { 46 g.globalRangeGenerator.Sync() 47 } 48 49 func (g *goroutineGenerator) GoroutineLabel(ctx *traceContext, ev *tracev2.Event) { 50 l := ev.Label() 51 g.gStates[l.Resource.Goroutine()].setLabel(l.Label) 52 } 53 54 func (g *goroutineGenerator) GoroutineRange(ctx *traceContext, ev *tracev2.Event) { 55 r := ev.Range() 56 switch ev.Kind() { 57 case tracev2.EventRangeBegin: 58 g.gStates[r.Scope.Goroutine()].rangeBegin(ev.Time(), r.Name, ev.Stack()) 59 case tracev2.EventRangeActive: 60 g.gStates[r.Scope.Goroutine()].rangeActive(r.Name) 61 case tracev2.EventRangeEnd: 62 gs := g.gStates[r.Scope.Goroutine()] 63 gs.rangeEnd(ev.Time(), r.Name, ev.Stack(), ctx) 64 } 65 } 66 67 func (g *goroutineGenerator) GoroutineTransition(ctx *traceContext, ev *tracev2.Event) { 68 st := ev.StateTransition() 69 goID := st.Resource.Goroutine() 70 71 // If we haven't seen this goroutine before, create a new 72 // gState for it. 73 gs, ok := g.gStates[goID] 74 if !ok { 75 gs = newGState[tracev2.GoID](goID) 76 g.gStates[goID] = gs 77 } 78 79 // Try to augment the name of the goroutine. 80 gs.augmentName(st.Stack) 81 82 // Handle the goroutine state transition. 83 from, to := st.Goroutine() 84 if from == to { 85 // Filter out no-op events. 86 return 87 } 88 if from.Executing() && !to.Executing() { 89 if to == tracev2.GoWaiting { 90 // Goroutine started blocking. 91 gs.block(ev.Time(), ev.Stack(), st.Reason, ctx) 92 } else { 93 gs.stop(ev.Time(), ev.Stack(), ctx) 94 } 95 } 96 if !from.Executing() && to.Executing() { 97 start := ev.Time() 98 if from == tracev2.GoUndetermined { 99 // Back-date the event to the start of the trace. 100 start = ctx.startTime 101 } 102 gs.start(start, goID, ctx) 103 } 104 105 if from == tracev2.GoWaiting { 106 // Goroutine unblocked. 107 gs.unblock(ev.Time(), ev.Stack(), ev.Goroutine(), ctx) 108 } 109 if from == tracev2.GoNotExist && to == tracev2.GoRunnable { 110 // Goroutine was created. 111 gs.created(ev.Time(), ev.Goroutine(), ev.Stack()) 112 } 113 if from == tracev2.GoSyscall && to != tracev2.GoRunning { 114 // Exiting blocked syscall. 115 gs.syscallEnd(ev.Time(), true, ctx) 116 gs.blockedSyscallEnd(ev.Time(), ev.Stack(), ctx) 117 } else if from == tracev2.GoSyscall { 118 // Check if we're exiting a syscall in a non-blocking way. 119 gs.syscallEnd(ev.Time(), false, ctx) 120 } 121 122 // Handle syscalls. 123 if to == tracev2.GoSyscall { 124 start := ev.Time() 125 if from == tracev2.GoUndetermined { 126 // Back-date the event to the start of the trace. 127 start = ctx.startTime 128 } 129 // Write down that we've entered a syscall. Note: we might have no G or P here 130 // if we're in a cgo callback or this is a transition from GoUndetermined 131 // (i.e. the G has been blocked in a syscall). 132 gs.syscallBegin(start, goID, ev.Stack()) 133 } 134 135 // Note down the goroutine transition. 136 _, inMarkAssist := gs.activeRanges["GC mark assist"] 137 ctx.GoroutineTransition(ctx.elapsed(ev.Time()), viewerGState(from, inMarkAssist), viewerGState(to, inMarkAssist)) 138 } 139 140 func (g *goroutineGenerator) ProcRange(ctx *traceContext, ev *tracev2.Event) { 141 // TODO(mknyszek): Extend procRangeGenerator to support rendering proc ranges 142 // that overlap with a goroutine's execution. 143 } 144 145 func (g *goroutineGenerator) ProcTransition(ctx *traceContext, ev *tracev2.Event) { 146 // Not needed. All relevant information for goroutines can be derived from goroutine transitions. 147 } 148 149 func (g *goroutineGenerator) Finish(ctx *traceContext) { 150 ctx.SetResourceType("G") 151 152 // Finish off global ranges. 153 g.globalRangeGenerator.Finish(ctx) 154 155 // Finish off all the goroutine slices. 156 for id, gs := range g.gStates { 157 gs.finish(ctx) 158 159 // Tell the emitter about the goroutines we want to render. 160 ctx.Resource(uint64(id), gs.name()) 161 } 162 163 // Set the goroutine to focus on. 164 if g.focus != tracev2.NoGoroutine { 165 ctx.Focus(uint64(g.focus)) 166 } 167 }