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  }