Source file src/runtime/traceruntime.go

     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  // Runtime -> tracer API.
     6  
     7  package runtime
     8  
     9  import (
    10  	"internal/runtime/atomic"
    11  	_ "unsafe" // for go:linkname
    12  )
    13  
    14  // gTraceState is per-G state for the tracer.
    15  type gTraceState struct {
    16  	traceSchedResourceState
    17  }
    18  
    19  // reset resets the gTraceState for a new goroutine.
    20  func (s *gTraceState) reset() {
    21  	s.seq = [2]uint64{}
    22  	// N.B. s.statusTraced is managed and cleared separately.
    23  }
    24  
    25  // mTraceState is per-M state for the tracer.
    26  type mTraceState struct {
    27  	seqlock       atomic.Uintptr                    // seqlock indicating that this M is writing to a trace buffer.
    28  	buf           [2][traceNumExperiments]*traceBuf // Per-M traceBuf for writing. Indexed by trace.gen%2.
    29  	link          *m                                // Snapshot of alllink or freelink.
    30  	reentered     uint32                            // Whether we've reentered tracing from within tracing.
    31  	oldthrowsplit bool                              // gp.throwsplit upon calling traceLocker.writer. For debugging.
    32  }
    33  
    34  // pTraceState is per-P state for the tracer.
    35  type pTraceState struct {
    36  	traceSchedResourceState
    37  
    38  	// mSyscallID is the ID of the M this was bound to before entering a syscall.
    39  	mSyscallID int64
    40  
    41  	// maySweep indicates the sweep events should be traced.
    42  	// This is used to defer the sweep start event until a span
    43  	// has actually been swept.
    44  	maySweep bool
    45  
    46  	// inSweep indicates that at least one sweep event has been traced.
    47  	inSweep bool
    48  
    49  	// swept and reclaimed track the number of bytes swept and reclaimed
    50  	// by sweeping in the current sweep loop (while maySweep was true).
    51  	swept, reclaimed uintptr
    52  }
    53  
    54  // traceLockInit initializes global trace locks.
    55  func traceLockInit() {
    56  	// Sharing a lock rank here is fine because they should never be accessed
    57  	// together. If they are, we want to find out immediately.
    58  	lockInit(&trace.stringTab[0].lock, lockRankTraceStrings)
    59  	lockInit(&trace.stringTab[0].tab.mem.lock, lockRankTraceStrings)
    60  	lockInit(&trace.stringTab[1].lock, lockRankTraceStrings)
    61  	lockInit(&trace.stringTab[1].tab.mem.lock, lockRankTraceStrings)
    62  	lockInit(&trace.stackTab[0].tab.mem.lock, lockRankTraceStackTab)
    63  	lockInit(&trace.stackTab[1].tab.mem.lock, lockRankTraceStackTab)
    64  	lockInit(&trace.typeTab[0].tab.mem.lock, lockRankTraceTypeTab)
    65  	lockInit(&trace.typeTab[1].tab.mem.lock, lockRankTraceTypeTab)
    66  	lockInit(&trace.lock, lockRankTrace)
    67  }
    68  
    69  // lockRankMayTraceFlush records the lock ranking effects of a
    70  // potential call to traceFlush.
    71  //
    72  // nosplit because traceAcquire is nosplit.
    73  //
    74  //go:nosplit
    75  func lockRankMayTraceFlush() {
    76  	lockWithRankMayAcquire(&trace.lock, getLockRank(&trace.lock))
    77  }
    78  
    79  // traceBlockReason is an enumeration of reasons a goroutine might block.
    80  // This is the interface the rest of the runtime uses to tell the
    81  // tracer why a goroutine blocked. The tracer then propagates this information
    82  // into the trace however it sees fit.
    83  //
    84  // Note that traceBlockReasons should not be compared, since reasons that are
    85  // distinct by name may *not* be distinct by value.
    86  type traceBlockReason uint8
    87  
    88  const (
    89  	traceBlockGeneric traceBlockReason = iota
    90  	traceBlockForever
    91  	traceBlockNet
    92  	traceBlockSelect
    93  	traceBlockCondWait
    94  	traceBlockSync
    95  	traceBlockChanSend
    96  	traceBlockChanRecv
    97  	traceBlockGCMarkAssist
    98  	traceBlockGCSweep
    99  	traceBlockSystemGoroutine
   100  	traceBlockPreempted
   101  	traceBlockDebugCall
   102  	traceBlockUntilGCEnds
   103  	traceBlockSleep
   104  	traceBlockGCWeakToStrongWait
   105  	traceBlockSynctest
   106  )
   107  
   108  var traceBlockReasonStrings = [...]string{
   109  	traceBlockGeneric:            "unspecified",
   110  	traceBlockForever:            "forever",
   111  	traceBlockNet:                "network",
   112  	traceBlockSelect:             "select",
   113  	traceBlockCondWait:           "sync.(*Cond).Wait",
   114  	traceBlockSync:               "sync",
   115  	traceBlockChanSend:           "chan send",
   116  	traceBlockChanRecv:           "chan receive",
   117  	traceBlockGCMarkAssist:       "GC mark assist wait for work",
   118  	traceBlockGCSweep:            "GC background sweeper wait",
   119  	traceBlockSystemGoroutine:    "system goroutine wait",
   120  	traceBlockPreempted:          "preempted",
   121  	traceBlockDebugCall:          "wait for debug call",
   122  	traceBlockUntilGCEnds:        "wait until GC ends",
   123  	traceBlockSleep:              "sleep",
   124  	traceBlockGCWeakToStrongWait: "GC weak to strong wait",
   125  	traceBlockSynctest:           "synctest",
   126  }
   127  
   128  // traceGoStopReason is an enumeration of reasons a goroutine might yield.
   129  //
   130  // Note that traceGoStopReasons should not be compared, since reasons that are
   131  // distinct by name may *not* be distinct by value.
   132  type traceGoStopReason uint8
   133  
   134  const (
   135  	traceGoStopGeneric traceGoStopReason = iota
   136  	traceGoStopGoSched
   137  	traceGoStopPreempted
   138  )
   139  
   140  var traceGoStopReasonStrings = [...]string{
   141  	traceGoStopGeneric:   "unspecified",
   142  	traceGoStopGoSched:   "runtime.Gosched",
   143  	traceGoStopPreempted: "preempted",
   144  }
   145  
   146  // traceEnabled returns true if the trace is currently enabled.
   147  //
   148  //go:nosplit
   149  func traceEnabled() bool {
   150  	return trace.enabled
   151  }
   152  
   153  // traceAllocFreeEnabled returns true if the trace is currently enabled
   154  // and alloc/free events are also enabled.
   155  //
   156  //go:nosplit
   157  func traceAllocFreeEnabled() bool {
   158  	return trace.enabledWithAllocFree
   159  }
   160  
   161  // traceShuttingDown returns true if the trace is currently shutting down.
   162  func traceShuttingDown() bool {
   163  	return trace.shutdown.Load()
   164  }
   165  
   166  // traceLocker represents an M writing trace events. While a traceLocker value
   167  // is valid, the tracer observes all operations on the G/M/P or trace events being
   168  // written as happening atomically.
   169  type traceLocker struct {
   170  	mp  *m
   171  	gen uintptr
   172  }
   173  
   174  // debugTraceReentrancy checks if the trace is reentrant.
   175  //
   176  // This is optional because throwing in a function makes it instantly
   177  // not inlineable, and we want traceAcquire to be inlineable for
   178  // low overhead when the trace is disabled.
   179  const debugTraceReentrancy = false
   180  
   181  // traceAcquire prepares this M for writing one or more trace events.
   182  //
   183  // nosplit because it's called on the syscall path when stack movement is forbidden.
   184  //
   185  //go:nosplit
   186  func traceAcquire() traceLocker {
   187  	if !traceEnabled() {
   188  		return traceLocker{}
   189  	}
   190  	return traceAcquireEnabled()
   191  }
   192  
   193  // traceAcquireEnabled is the traceEnabled path for traceAcquire. It's explicitly
   194  // broken out to make traceAcquire inlineable to keep the overhead of the tracer
   195  // when it's disabled low.
   196  //
   197  // nosplit because it's called by traceAcquire, which is nosplit.
   198  //
   199  //go:nosplit
   200  func traceAcquireEnabled() traceLocker {
   201  	// Any time we acquire a traceLocker, we may flush a trace buffer. But
   202  	// buffer flushes are rare. Record the lock edge even if it doesn't happen
   203  	// this time.
   204  	lockRankMayTraceFlush()
   205  
   206  	// Prevent preemption.
   207  	mp := acquirem()
   208  
   209  	// Check if we're already tracing. It's safe to be reentrant in general,
   210  	// because this function (and the invariants of traceLocker.writer) ensure
   211  	// that it is.
   212  	if mp.trace.seqlock.Load()%2 == 1 {
   213  		mp.trace.reentered++
   214  		return traceLocker{mp, trace.gen.Load()}
   215  	}
   216  
   217  	// Acquire the trace seqlock. This prevents traceAdvance from moving forward
   218  	// until all Ms are observed to be outside of their seqlock critical section.
   219  	//
   220  	// Note: The seqlock is mutated here and also in traceCPUSample. If you update
   221  	// usage of the seqlock here, make sure to also look at what traceCPUSample is
   222  	// doing.
   223  	seq := mp.trace.seqlock.Add(1)
   224  	if debugTraceReentrancy && seq%2 != 1 {
   225  		throw("bad use of trace.seqlock")
   226  	}
   227  
   228  	// N.B. This load of gen appears redundant with the one in traceEnabled.
   229  	// However, it's very important that the gen we use for writing to the trace
   230  	// is acquired under a traceLocker so traceAdvance can make sure no stale
   231  	// gen values are being used.
   232  	//
   233  	// Because we're doing this load again, it also means that the trace
   234  	// might end up being disabled when we load it. In that case we need to undo
   235  	// what we did and bail.
   236  	gen := trace.gen.Load()
   237  	if gen == 0 {
   238  		mp.trace.seqlock.Add(1)
   239  		releasem(mp)
   240  		return traceLocker{}
   241  	}
   242  	return traceLocker{mp, gen}
   243  }
   244  
   245  // ok returns true if the traceLocker is valid (i.e. tracing is enabled).
   246  //
   247  // nosplit because it's called on the syscall path when stack movement is forbidden.
   248  //
   249  //go:nosplit
   250  func (tl traceLocker) ok() bool {
   251  	return tl.gen != 0
   252  }
   253  
   254  // traceRelease indicates that this M is done writing trace events.
   255  //
   256  // nosplit because it's called on the syscall path when stack movement is forbidden.
   257  //
   258  //go:nosplit
   259  func traceRelease(tl traceLocker) {
   260  	if tl.mp.trace.reentered > 0 {
   261  		tl.mp.trace.reentered--
   262  	} else {
   263  		seq := tl.mp.trace.seqlock.Add(1)
   264  		if debugTraceReentrancy && seq%2 != 0 {
   265  			print("runtime: seq=", seq, "\n")
   266  			throw("bad use of trace.seqlock")
   267  		}
   268  	}
   269  	releasem(tl.mp)
   270  }
   271  
   272  // traceExitingSyscall marks a goroutine as exiting the syscall slow path.
   273  //
   274  // Must be paired with a traceExitedSyscall call.
   275  func traceExitingSyscall() {
   276  	trace.exitingSyscall.Add(1)
   277  }
   278  
   279  // traceExitedSyscall marks a goroutine as having exited the syscall slow path.
   280  func traceExitedSyscall() {
   281  	trace.exitingSyscall.Add(-1)
   282  }
   283  
   284  // Gomaxprocs emits a ProcsChange event.
   285  func (tl traceLocker) Gomaxprocs(procs int32) {
   286  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvProcsChange, traceArg(procs), tl.stack(1))
   287  }
   288  
   289  // ProcStart traces a ProcStart event.
   290  //
   291  // Must be called with a valid P.
   292  func (tl traceLocker) ProcStart() {
   293  	pp := tl.mp.p.ptr()
   294  	// Procs are typically started within the scheduler when there is no user goroutine. If there is a user goroutine,
   295  	// it must be in _Gsyscall because the only time a goroutine is allowed to have its Proc moved around from under it
   296  	// is during a syscall.
   297  	tl.eventWriter(traceGoSyscall, traceProcIdle).event(traceEvProcStart, traceArg(pp.id), pp.trace.nextSeq(tl.gen))
   298  }
   299  
   300  // ProcStop traces a ProcStop event.
   301  func (tl traceLocker) ProcStop(pp *p) {
   302  	// The only time a goroutine is allowed to have its Proc moved around
   303  	// from under it is during a syscall.
   304  	tl.eventWriter(traceGoSyscall, traceProcRunning).event(traceEvProcStop)
   305  }
   306  
   307  // GCActive traces a GCActive event.
   308  //
   309  // Must be emitted by an actively running goroutine on an active P. This restriction can be changed
   310  // easily and only depends on where it's currently called.
   311  func (tl traceLocker) GCActive() {
   312  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCActive, traceArg(trace.seqGC))
   313  	// N.B. Only one GC can be running at a time, so this is naturally
   314  	// serialized by the caller.
   315  	trace.seqGC++
   316  }
   317  
   318  // GCStart traces a GCBegin event.
   319  //
   320  // Must be emitted by an actively running goroutine on an active P. This restriction can be changed
   321  // easily and only depends on where it's currently called.
   322  func (tl traceLocker) GCStart() {
   323  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCBegin, traceArg(trace.seqGC), tl.stack(3))
   324  	// N.B. Only one GC can be running at a time, so this is naturally
   325  	// serialized by the caller.
   326  	trace.seqGC++
   327  }
   328  
   329  // GCDone traces a GCEnd event.
   330  //
   331  // Must be emitted by an actively running goroutine on an active P. This restriction can be changed
   332  // easily and only depends on where it's currently called.
   333  func (tl traceLocker) GCDone() {
   334  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCEnd, traceArg(trace.seqGC))
   335  	// N.B. Only one GC can be running at a time, so this is naturally
   336  	// serialized by the caller.
   337  	trace.seqGC++
   338  }
   339  
   340  // STWStart traces a STWBegin event.
   341  func (tl traceLocker) STWStart(reason stwReason) {
   342  	// Although the current P may be in _Pgcstop here, we model the P as running during the STW. This deviates from the
   343  	// runtime's state tracking, but it's more accurate and doesn't result in any loss of information.
   344  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSTWBegin, tl.string(reason.String()), tl.stack(2))
   345  }
   346  
   347  // STWDone traces a STWEnd event.
   348  func (tl traceLocker) STWDone() {
   349  	// Although the current P may be in _Pgcstop here, we model the P as running during the STW. This deviates from the
   350  	// runtime's state tracking, but it's more accurate and doesn't result in any loss of information.
   351  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSTWEnd)
   352  }
   353  
   354  // GCSweepStart prepares to trace a sweep loop. This does not
   355  // emit any events until traceGCSweepSpan is called.
   356  //
   357  // GCSweepStart must be paired with traceGCSweepDone and there
   358  // must be no preemption points between these two calls.
   359  //
   360  // Must be called with a valid P.
   361  func (tl traceLocker) GCSweepStart() {
   362  	// Delay the actual GCSweepBegin event until the first span
   363  	// sweep. If we don't sweep anything, don't emit any events.
   364  	pp := tl.mp.p.ptr()
   365  	if pp.trace.maySweep {
   366  		throw("double traceGCSweepStart")
   367  	}
   368  	pp.trace.maySweep, pp.trace.swept, pp.trace.reclaimed = true, 0, 0
   369  }
   370  
   371  // GCSweepSpan traces the sweep of a single span. If this is
   372  // the first span swept since traceGCSweepStart was called, this
   373  // will emit a GCSweepBegin event.
   374  //
   375  // This may be called outside a traceGCSweepStart/traceGCSweepDone
   376  // pair; however, it will not emit any trace events in this case.
   377  //
   378  // Must be called with a valid P.
   379  func (tl traceLocker) GCSweepSpan(bytesSwept uintptr) {
   380  	pp := tl.mp.p.ptr()
   381  	if pp.trace.maySweep {
   382  		if pp.trace.swept == 0 {
   383  			tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCSweepBegin, tl.stack(1))
   384  			pp.trace.inSweep = true
   385  		}
   386  		pp.trace.swept += bytesSwept
   387  	}
   388  }
   389  
   390  // GCSweepDone finishes tracing a sweep loop. If any memory was
   391  // swept (i.e. traceGCSweepSpan emitted an event) then this will emit
   392  // a GCSweepEnd event.
   393  //
   394  // Must be called with a valid P.
   395  func (tl traceLocker) GCSweepDone() {
   396  	pp := tl.mp.p.ptr()
   397  	if !pp.trace.maySweep {
   398  		throw("missing traceGCSweepStart")
   399  	}
   400  	if pp.trace.inSweep {
   401  		tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCSweepEnd, traceArg(pp.trace.swept), traceArg(pp.trace.reclaimed))
   402  		pp.trace.inSweep = false
   403  	}
   404  	pp.trace.maySweep = false
   405  }
   406  
   407  // GCMarkAssistStart emits a MarkAssistBegin event.
   408  func (tl traceLocker) GCMarkAssistStart() {
   409  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCMarkAssistBegin, tl.stack(1))
   410  }
   411  
   412  // GCMarkAssistDone emits a MarkAssistEnd event.
   413  func (tl traceLocker) GCMarkAssistDone() {
   414  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCMarkAssistEnd)
   415  }
   416  
   417  // GoCreate emits a GoCreate event.
   418  func (tl traceLocker) GoCreate(newg *g, pc uintptr, blocked bool) {
   419  	newg.trace.setStatusTraced(tl.gen)
   420  	ev := traceEvGoCreate
   421  	if blocked {
   422  		ev = traceEvGoCreateBlocked
   423  	}
   424  	tl.eventWriter(traceGoRunning, traceProcRunning).event(ev, traceArg(newg.goid), tl.startPC(pc), tl.stack(2))
   425  }
   426  
   427  // GoStart emits a GoStart event.
   428  //
   429  // Must be called with a valid P.
   430  func (tl traceLocker) GoStart() {
   431  	gp := getg().m.curg
   432  	pp := gp.m.p
   433  	w := tl.eventWriter(traceGoRunnable, traceProcRunning)
   434  	w.event(traceEvGoStart, traceArg(gp.goid), gp.trace.nextSeq(tl.gen))
   435  	if pp.ptr().gcMarkWorkerMode != gcMarkWorkerNotWorker {
   436  		w.event(traceEvGoLabel, trace.markWorkerLabels[tl.gen%2][pp.ptr().gcMarkWorkerMode])
   437  	}
   438  }
   439  
   440  // GoEnd emits a GoDestroy event.
   441  //
   442  // TODO(mknyszek): Rename this to GoDestroy.
   443  func (tl traceLocker) GoEnd() {
   444  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoDestroy)
   445  }
   446  
   447  // GoSched emits a GoStop event with a GoSched reason.
   448  func (tl traceLocker) GoSched() {
   449  	tl.GoStop(traceGoStopGoSched)
   450  }
   451  
   452  // GoPreempt emits a GoStop event with a GoPreempted reason.
   453  func (tl traceLocker) GoPreempt() {
   454  	tl.GoStop(traceGoStopPreempted)
   455  }
   456  
   457  // GoStop emits a GoStop event with the provided reason.
   458  func (tl traceLocker) GoStop(reason traceGoStopReason) {
   459  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoStop, traceArg(trace.goStopReasons[tl.gen%2][reason]), tl.stack(1))
   460  }
   461  
   462  // GoPark emits a GoBlock event with the provided reason.
   463  //
   464  // TODO(mknyszek): Replace traceBlockReason with waitReason. It's silly
   465  // that we have both, and waitReason is way more descriptive.
   466  func (tl traceLocker) GoPark(reason traceBlockReason, skip int) {
   467  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoBlock, traceArg(trace.goBlockReasons[tl.gen%2][reason]), tl.stack(skip))
   468  }
   469  
   470  // GoUnpark emits a GoUnblock event.
   471  func (tl traceLocker) GoUnpark(gp *g, skip int) {
   472  	// Emit a GoWaiting status if necessary for the unblocked goroutine.
   473  	tl.emitUnblockStatus(gp, tl.gen)
   474  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoUnblock, traceArg(gp.goid), gp.trace.nextSeq(tl.gen), tl.stack(skip))
   475  }
   476  
   477  // GoSwitch emits a GoSwitch event. If destroy is true, the calling goroutine
   478  // is simultaneously being destroyed.
   479  func (tl traceLocker) GoSwitch(nextg *g, destroy bool) {
   480  	// Emit a GoWaiting status if necessary for the unblocked goroutine.
   481  	tl.emitUnblockStatus(nextg, tl.gen)
   482  	w := tl.eventWriter(traceGoRunning, traceProcRunning)
   483  	ev := traceEvGoSwitch
   484  	if destroy {
   485  		ev = traceEvGoSwitchDestroy
   486  	}
   487  	w.event(ev, traceArg(nextg.goid), nextg.trace.nextSeq(tl.gen))
   488  }
   489  
   490  // emitUnblockStatus emits a GoStatus GoWaiting event for a goroutine about to be
   491  // unblocked to the trace writer.
   492  func (tl traceLocker) emitUnblockStatus(gp *g, gen uintptr) {
   493  	if !gp.trace.statusWasTraced(gen) && gp.trace.acquireStatus(gen) {
   494  		// TODO(go.dev/issue/65634): Although it would be nice to add a stack trace here of gp,
   495  		// we cannot safely do so. gp is in _Gwaiting and so we don't have ownership of its stack.
   496  		// We can fix this by acquiring the goroutine's scan bit.
   497  		tl.writer().writeGoStatus(gp.goid, -1, traceGoWaiting, gp.inMarkAssist, 0).end()
   498  	}
   499  }
   500  
   501  // GoSysCall emits a GoSyscallBegin event.
   502  //
   503  // Must be called with a valid P.
   504  func (tl traceLocker) GoSysCall() {
   505  	// Scribble down the M that the P is currently attached to.
   506  	pp := tl.mp.p.ptr()
   507  	pp.trace.mSyscallID = int64(tl.mp.procid)
   508  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoSyscallBegin, pp.trace.nextSeq(tl.gen), tl.stack(1))
   509  }
   510  
   511  // GoSysExit emits a GoSyscallEnd event, possibly along with a GoSyscallBlocked event
   512  // if lostP is true.
   513  //
   514  // lostP must be true in all cases that a goroutine loses its P during a syscall.
   515  // This means it's not sufficient to check if it has no P. In particular, it needs to be
   516  // true in the following cases:
   517  // - The goroutine lost its P, it ran some other code, and then got it back. It's now running with that P.
   518  // - The goroutine lost its P and was unable to reacquire it, and is now running without a P.
   519  // - The goroutine lost its P and acquired a different one, and is now running with that P.
   520  func (tl traceLocker) GoSysExit(lostP bool) {
   521  	ev := traceEvGoSyscallEnd
   522  	procStatus := traceProcSyscall // Procs implicitly enter traceProcSyscall on GoSyscallBegin.
   523  	if lostP {
   524  		ev = traceEvGoSyscallEndBlocked
   525  		procStatus = traceProcRunning // If a G has a P when emitting this event, it reacquired a P and is indeed running.
   526  	} else {
   527  		tl.mp.p.ptr().trace.mSyscallID = -1
   528  	}
   529  	tl.eventWriter(traceGoSyscall, procStatus).event(ev)
   530  }
   531  
   532  // ProcSteal indicates that our current M stole a P from another M.
   533  //
   534  // inSyscall indicates that we're stealing the P from a syscall context.
   535  //
   536  // The caller must have ownership of pp.
   537  func (tl traceLocker) ProcSteal(pp *p, inSyscall bool) {
   538  	// Grab the M ID we stole from.
   539  	mStolenFrom := pp.trace.mSyscallID
   540  	pp.trace.mSyscallID = -1
   541  
   542  	// Emit the status of the P we're stealing. We may be just about to do this when creating the event
   543  	// writer but it's not guaranteed, even if inSyscall is true. Although it might seem like from a
   544  	// syscall context we're always stealing a P for ourselves, we may have not wired it up yet (so
   545  	// it wouldn't be visible to eventWriter) or we may not even intend to wire it up to ourselves
   546  	// at all (e.g. entersyscall_gcwait).
   547  	if !pp.trace.statusWasTraced(tl.gen) && pp.trace.acquireStatus(tl.gen) {
   548  		// Careful: don't use the event writer. We never want status or in-progress events
   549  		// to trigger more in-progress events.
   550  		tl.writer().writeProcStatus(uint64(pp.id), traceProcSyscallAbandoned, pp.trace.inSweep).end()
   551  	}
   552  
   553  	// The status of the proc and goroutine, if we need to emit one here, is not evident from the
   554  	// context of just emitting this event alone. There are two cases. Either we're trying to steal
   555  	// the P just to get its attention (e.g. STW or sysmon retake) or we're trying to steal a P for
   556  	// ourselves specifically to keep running. The two contexts look different, but can be summarized
   557  	// fairly succinctly. In the former, we're a regular running goroutine and proc, if we have either.
   558  	// In the latter, we're a goroutine in a syscall.
   559  	goStatus := traceGoRunning
   560  	procStatus := traceProcRunning
   561  	if inSyscall {
   562  		goStatus = traceGoSyscall
   563  		procStatus = traceProcSyscallAbandoned
   564  	}
   565  	tl.eventWriter(goStatus, procStatus).event(traceEvProcSteal, traceArg(pp.id), pp.trace.nextSeq(tl.gen), traceArg(mStolenFrom))
   566  }
   567  
   568  // HeapAlloc emits a HeapAlloc event.
   569  func (tl traceLocker) HeapAlloc(live uint64) {
   570  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapAlloc, traceArg(live))
   571  }
   572  
   573  // HeapGoal reads the current heap goal and emits a HeapGoal event.
   574  func (tl traceLocker) HeapGoal() {
   575  	heapGoal := gcController.heapGoal()
   576  	if heapGoal == ^uint64(0) {
   577  		// Heap-based triggering is disabled.
   578  		heapGoal = 0
   579  	}
   580  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapGoal, traceArg(heapGoal))
   581  }
   582  
   583  // GoCreateSyscall indicates that a goroutine has transitioned from dead to GoSyscall.
   584  //
   585  // Unlike GoCreate, the caller must be running on gp.
   586  //
   587  // This occurs when C code calls into Go. On pthread platforms it occurs only when
   588  // a C thread calls into Go code for the first time.
   589  func (tl traceLocker) GoCreateSyscall(gp *g) {
   590  	// N.B. We should never trace a status for this goroutine (which we're currently running on),
   591  	// since we want this to appear like goroutine creation.
   592  	gp.trace.setStatusTraced(tl.gen)
   593  	tl.eventWriter(traceGoBad, traceProcBad).event(traceEvGoCreateSyscall, traceArg(gp.goid))
   594  }
   595  
   596  // GoDestroySyscall indicates that a goroutine has transitioned from GoSyscall to dead.
   597  //
   598  // Must not have a P.
   599  //
   600  // This occurs when Go code returns back to C. On pthread platforms it occurs only when
   601  // the C thread is destroyed.
   602  func (tl traceLocker) GoDestroySyscall() {
   603  	// N.B. If we trace a status here, we must never have a P, and we must be on a goroutine
   604  	// that is in the syscall state.
   605  	tl.eventWriter(traceGoSyscall, traceProcBad).event(traceEvGoDestroySyscall)
   606  }
   607  
   608  // To access runtime functions from runtime/trace.
   609  // See runtime/trace/annotation.go
   610  
   611  // trace_userTaskCreate emits a UserTaskCreate event.
   612  //
   613  //go:linkname trace_userTaskCreate runtime/trace.userTaskCreate
   614  func trace_userTaskCreate(id, parentID uint64, taskType string) {
   615  	tl := traceAcquire()
   616  	if !tl.ok() {
   617  		// Need to do this check because the caller won't have it.
   618  		return
   619  	}
   620  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvUserTaskBegin, traceArg(id), traceArg(parentID), tl.string(taskType), tl.stack(3))
   621  	traceRelease(tl)
   622  }
   623  
   624  // trace_userTaskEnd emits a UserTaskEnd event.
   625  //
   626  //go:linkname trace_userTaskEnd runtime/trace.userTaskEnd
   627  func trace_userTaskEnd(id uint64) {
   628  	tl := traceAcquire()
   629  	if !tl.ok() {
   630  		// Need to do this check because the caller won't have it.
   631  		return
   632  	}
   633  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvUserTaskEnd, traceArg(id), tl.stack(2))
   634  	traceRelease(tl)
   635  }
   636  
   637  // trace_userRegion emits a UserRegionBegin or UserRegionEnd event,
   638  // depending on mode (0 == Begin, 1 == End).
   639  //
   640  // TODO(mknyszek): Just make this two functions.
   641  //
   642  //go:linkname trace_userRegion runtime/trace.userRegion
   643  func trace_userRegion(id, mode uint64, name string) {
   644  	tl := traceAcquire()
   645  	if !tl.ok() {
   646  		// Need to do this check because the caller won't have it.
   647  		return
   648  	}
   649  	var ev traceEv
   650  	switch mode {
   651  	case 0:
   652  		ev = traceEvUserRegionBegin
   653  	case 1:
   654  		ev = traceEvUserRegionEnd
   655  	default:
   656  		return
   657  	}
   658  	tl.eventWriter(traceGoRunning, traceProcRunning).event(ev, traceArg(id), tl.string(name), tl.stack(3))
   659  	traceRelease(tl)
   660  }
   661  
   662  // trace_userLog emits a UserRegionBegin or UserRegionEnd event.
   663  //
   664  //go:linkname trace_userLog runtime/trace.userLog
   665  func trace_userLog(id uint64, category, message string) {
   666  	tl := traceAcquire()
   667  	if !tl.ok() {
   668  		// Need to do this check because the caller won't have it.
   669  		return
   670  	}
   671  	tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvUserLog, traceArg(id), tl.string(category), tl.uniqueString(message), tl.stack(3))
   672  	traceRelease(tl)
   673  }
   674  
   675  // traceThreadDestroy is called when a thread is removed from
   676  // sched.freem.
   677  //
   678  // mp must not be able to emit trace events anymore.
   679  //
   680  // sched.lock must be held to synchronize with traceAdvance.
   681  func traceThreadDestroy(mp *m) {
   682  	assertLockHeld(&sched.lock)
   683  
   684  	// Flush all outstanding buffers to maintain the invariant
   685  	// that an M only has active buffers while on sched.freem
   686  	// or allm.
   687  	//
   688  	// Perform a traceAcquire/traceRelease on behalf of mp to
   689  	// synchronize with the tracer trying to flush our buffer
   690  	// as well.
   691  	seq := mp.trace.seqlock.Add(1)
   692  	if debugTraceReentrancy && seq%2 != 1 {
   693  		throw("bad use of trace.seqlock")
   694  	}
   695  	systemstack(func() {
   696  		lock(&trace.lock)
   697  		for i := range mp.trace.buf {
   698  			for exp, buf := range mp.trace.buf[i] {
   699  				if buf != nil {
   700  					// N.B. traceBufFlush accepts a generation, but it
   701  					// really just cares about gen%2.
   702  					traceBufFlush(buf, uintptr(i))
   703  					mp.trace.buf[i][exp] = nil
   704  				}
   705  			}
   706  		}
   707  		unlock(&trace.lock)
   708  	})
   709  	seq1 := mp.trace.seqlock.Add(1)
   710  	if seq1 != seq+1 {
   711  		print("runtime: seq1=", seq1, "\n")
   712  		throw("bad use of trace.seqlock")
   713  	}
   714  }
   715  

View as plain text