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

View as plain text