github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/trace/v2/procgen.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  	"fmt"
     9  
    10  	"github.com/go-asm/go/trace/traceviewer"
    11  	"github.com/go-asm/go/trace/traceviewer/format"
    12  	tracev2 "github.com/go-asm/go/trace/v2"
    13  )
    14  
    15  var _ generator = &procGenerator{}
    16  
    17  type procGenerator struct {
    18  	globalRangeGenerator
    19  	globalMetricGenerator
    20  	procRangeGenerator
    21  	stackSampleGenerator[tracev2.ProcID]
    22  	logEventGenerator[tracev2.ProcID]
    23  
    24  	gStates   map[tracev2.GoID]*gState[tracev2.ProcID]
    25  	inSyscall map[tracev2.ProcID]*gState[tracev2.ProcID]
    26  	maxProc   tracev2.ProcID
    27  }
    28  
    29  func newProcGenerator() *procGenerator {
    30  	pg := new(procGenerator)
    31  	rg := func(ev *tracev2.Event) tracev2.ProcID {
    32  		return ev.Proc()
    33  	}
    34  	pg.stackSampleGenerator.getResource = rg
    35  	pg.logEventGenerator.getResource = rg
    36  	pg.gStates = make(map[tracev2.GoID]*gState[tracev2.ProcID])
    37  	pg.inSyscall = make(map[tracev2.ProcID]*gState[tracev2.ProcID])
    38  	return pg
    39  }
    40  
    41  func (g *procGenerator) Sync() {
    42  	g.globalRangeGenerator.Sync()
    43  	g.procRangeGenerator.Sync()
    44  }
    45  
    46  func (g *procGenerator) GoroutineLabel(ctx *traceContext, ev *tracev2.Event) {
    47  	l := ev.Label()
    48  	g.gStates[l.Resource.Goroutine()].setLabel(l.Label)
    49  }
    50  
    51  func (g *procGenerator) GoroutineRange(ctx *traceContext, ev *tracev2.Event) {
    52  	r := ev.Range()
    53  	switch ev.Kind() {
    54  	case tracev2.EventRangeBegin:
    55  		g.gStates[r.Scope.Goroutine()].rangeBegin(ev.Time(), r.Name, ev.Stack())
    56  	case tracev2.EventRangeActive:
    57  		g.gStates[r.Scope.Goroutine()].rangeActive(r.Name)
    58  	case tracev2.EventRangeEnd:
    59  		gs := g.gStates[r.Scope.Goroutine()]
    60  		gs.rangeEnd(ev.Time(), r.Name, ev.Stack(), ctx)
    61  	}
    62  }
    63  
    64  func (g *procGenerator) GoroutineTransition(ctx *traceContext, ev *tracev2.Event) {
    65  	st := ev.StateTransition()
    66  	goID := st.Resource.Goroutine()
    67  
    68  	// If we haven't seen this goroutine before, create a new
    69  	// gState for it.
    70  	gs, ok := g.gStates[goID]
    71  	if !ok {
    72  		gs = newGState[tracev2.ProcID](goID)
    73  		g.gStates[goID] = gs
    74  	}
    75  	// If we haven't already named this goroutine, try to name it.
    76  	gs.augmentName(st.Stack)
    77  
    78  	// Handle the goroutine state transition.
    79  	from, to := st.Goroutine()
    80  	if from == to {
    81  		// Filter out no-op events.
    82  		return
    83  	}
    84  	if from == tracev2.GoRunning && !to.Executing() {
    85  		if to == tracev2.GoWaiting {
    86  			// Goroutine started blocking.
    87  			gs.block(ev.Time(), ev.Stack(), st.Reason, ctx)
    88  		} else {
    89  			gs.stop(ev.Time(), ev.Stack(), ctx)
    90  		}
    91  	}
    92  	if !from.Executing() && to == tracev2.GoRunning {
    93  		start := ev.Time()
    94  		if from == tracev2.GoUndetermined {
    95  			// Back-date the event to the start of the trace.
    96  			start = ctx.startTime
    97  		}
    98  		gs.start(start, ev.Proc(), ctx)
    99  	}
   100  
   101  	if from == tracev2.GoWaiting {
   102  		// Goroutine was unblocked.
   103  		gs.unblock(ev.Time(), ev.Stack(), ev.Proc(), ctx)
   104  	}
   105  	if from == tracev2.GoNotExist && to == tracev2.GoRunnable {
   106  		// Goroutine was created.
   107  		gs.created(ev.Time(), ev.Proc(), ev.Stack())
   108  	}
   109  	if from == tracev2.GoSyscall && to != tracev2.GoRunning {
   110  		// Goroutine exited a blocked syscall.
   111  		gs.blockedSyscallEnd(ev.Time(), ev.Stack(), ctx)
   112  	}
   113  
   114  	// Handle syscalls.
   115  	if to == tracev2.GoSyscall && ev.Proc() != tracev2.NoProc {
   116  		start := ev.Time()
   117  		if from == tracev2.GoUndetermined {
   118  			// Back-date the event to the start of the trace.
   119  			start = ctx.startTime
   120  		}
   121  		// Write down that we've entered a syscall. Note: we might have no P here
   122  		// if we're in a cgo callback or this is a transition from GoUndetermined
   123  		// (i.e. the G has been blocked in a syscall).
   124  		gs.syscallBegin(start, ev.Proc(), ev.Stack())
   125  		g.inSyscall[ev.Proc()] = gs
   126  	}
   127  	// Check if we're exiting a non-blocking syscall.
   128  	_, didNotBlock := g.inSyscall[ev.Proc()]
   129  	if from == tracev2.GoSyscall && didNotBlock {
   130  		gs.syscallEnd(ev.Time(), false, ctx)
   131  		delete(g.inSyscall, ev.Proc())
   132  	}
   133  
   134  	// Note down the goroutine transition.
   135  	_, inMarkAssist := gs.activeRanges["GC mark assist"]
   136  	ctx.GoroutineTransition(ctx.elapsed(ev.Time()), viewerGState(from, inMarkAssist), viewerGState(to, inMarkAssist))
   137  }
   138  
   139  func (g *procGenerator) ProcTransition(ctx *traceContext, ev *tracev2.Event) {
   140  	st := ev.StateTransition()
   141  	proc := st.Resource.Proc()
   142  
   143  	g.maxProc = max(g.maxProc, proc)
   144  	viewerEv := traceviewer.InstantEvent{
   145  		Resource: uint64(proc),
   146  		Stack:    ctx.Stack(viewerFrames(ev.Stack())),
   147  	}
   148  
   149  	from, to := st.Proc()
   150  	if from == to {
   151  		// Filter out no-op events.
   152  		return
   153  	}
   154  	if to.Executing() {
   155  		start := ev.Time()
   156  		if from == tracev2.ProcUndetermined {
   157  			start = ctx.startTime
   158  		}
   159  		viewerEv.Name = "proc start"
   160  		viewerEv.Arg = format.ThreadIDArg{ThreadID: uint64(ev.Thread())}
   161  		viewerEv.Ts = ctx.elapsed(start)
   162  		ctx.IncThreadStateCount(ctx.elapsed(start), traceviewer.ThreadStateRunning, 1)
   163  	}
   164  	if from.Executing() {
   165  		start := ev.Time()
   166  		viewerEv.Name = "proc stop"
   167  		viewerEv.Ts = ctx.elapsed(start)
   168  		ctx.IncThreadStateCount(ctx.elapsed(start), traceviewer.ThreadStateRunning, -1)
   169  
   170  		// Check if this proc was in a syscall before it stopped.
   171  		// This means the syscall blocked. We need to emit it to the
   172  		// viewer at this point because we only display the time the
   173  		// syscall occupied a P when the viewer is in per-P mode.
   174  		//
   175  		// TODO(mknyszek): We could do better in a per-M mode because
   176  		// all events have to happen on *some* thread, and in v2 traces
   177  		// we know what that thread is.
   178  		gs, ok := g.inSyscall[proc]
   179  		if ok {
   180  			// Emit syscall slice for blocked syscall.
   181  			gs.syscallEnd(start, true, ctx)
   182  			gs.stop(start, ev.Stack(), ctx)
   183  			delete(g.inSyscall, proc)
   184  		}
   185  	}
   186  	// TODO(mknyszek): Consider modeling procs differently and have them be
   187  	// transition to and from NotExist when GOMAXPROCS changes. We can emit
   188  	// events for this to clearly delineate GOMAXPROCS changes.
   189  
   190  	if viewerEv.Name != "" {
   191  		ctx.Instant(viewerEv)
   192  	}
   193  }
   194  
   195  func (g *procGenerator) Finish(ctx *traceContext) {
   196  	ctx.SetResourceType("PROCS")
   197  
   198  	// Finish off ranges first. It doesn't really matter for the global ranges,
   199  	// but the proc ranges need to either be a subset of a goroutine slice or
   200  	// their own slice entirely. If the former, it needs to end first.
   201  	g.procRangeGenerator.Finish(ctx)
   202  	g.globalRangeGenerator.Finish(ctx)
   203  
   204  	// Finish off all the goroutine slices.
   205  	for _, gs := range g.gStates {
   206  		gs.finish(ctx)
   207  	}
   208  
   209  	// Name all the procs to the emitter.
   210  	for i := uint64(0); i <= uint64(g.maxProc); i++ {
   211  		ctx.Resource(i, fmt.Sprintf("Proc %v", i))
   212  	}
   213  }