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 5package main 6 7import ( 8 "internal/trace" 9) 10 11var _ generator = &goroutineGenerator{} 12 13type goroutineGenerator struct { 14 globalRangeGenerator 15 globalMetricGenerator 16 stackSampleGenerator[trace.GoID] 17 logEventGenerator[trace.GoID] 18 19 gStates map[trace.GoID]*gState[trace.GoID] 20 focus trace.GoID 21 filter map[trace.GoID]struct{} 22} 23 24func newGoroutineGenerator(ctx *traceContext, focus trace.GoID, filter map[trace.GoID]struct{}) *goroutineGenerator { 25 gg := new(goroutineGenerator) 26 rg := func(ev *trace.Event) trace.GoID { 27 return ev.Goroutine() 28 } 29 gg.stackSampleGenerator.getResource = rg 30 gg.logEventGenerator.getResource = rg 31 gg.gStates = make(map[trace.GoID]*gState[trace.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[trace.GoID(resource)] 39 return ok 40 }) 41 } 42 return gg 43} 44 45func (g *goroutineGenerator) Sync() { 46 g.globalRangeGenerator.Sync() 47} 48 49func (g *goroutineGenerator) GoroutineLabel(ctx *traceContext, ev *trace.Event) { 50 l := ev.Label() 51 g.gStates[l.Resource.Goroutine()].setLabel(l.Label) 52} 53 54func (g *goroutineGenerator) GoroutineRange(ctx *traceContext, ev *trace.Event) { 55 r := ev.Range() 56 switch ev.Kind() { 57 case trace.EventRangeBegin: 58 g.gStates[r.Scope.Goroutine()].rangeBegin(ev.Time(), r.Name, ev.Stack()) 59 case trace.EventRangeActive: 60 g.gStates[r.Scope.Goroutine()].rangeActive(r.Name) 61 case trace.EventRangeEnd: 62 gs := g.gStates[r.Scope.Goroutine()] 63 gs.rangeEnd(ev.Time(), r.Name, ev.Stack(), ctx) 64 } 65} 66 67func (g *goroutineGenerator) GoroutineTransition(ctx *traceContext, ev *trace.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[trace.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 == trace.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 == trace.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 == trace.GoWaiting { 106 // Goroutine unblocked. 107 gs.unblock(ev.Time(), ev.Stack(), ev.Goroutine(), ctx) 108 } 109 if from == trace.GoNotExist && to == trace.GoRunnable { 110 // Goroutine was created. 111 gs.created(ev.Time(), ev.Goroutine(), ev.Stack()) 112 } 113 if from == trace.GoSyscall && to != trace.GoRunning { 114 // Exiting blocked syscall. 115 gs.syscallEnd(ev.Time(), true, ctx) 116 gs.blockedSyscallEnd(ev.Time(), ev.Stack(), ctx) 117 } else if from == trace.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 == trace.GoSyscall { 124 start := ev.Time() 125 if from == trace.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 140func (g *goroutineGenerator) ProcRange(ctx *traceContext, ev *trace.Event) { 141 // TODO(mknyszek): Extend procRangeGenerator to support rendering proc ranges 142 // that overlap with a goroutine's execution. 143} 144 145func (g *goroutineGenerator) ProcTransition(ctx *traceContext, ev *trace.Event) { 146 // Not needed. All relevant information for goroutines can be derived from goroutine transitions. 147} 148 149func (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 != trace.NoGoroutine { 165 ctx.Focus(uint64(g.focus)) 166 } 167} 168