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

View as plain text