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