diff options
| author | Michael Anthony Knyszek <mknyszek@google.com> | 2024-04-24 16:26:39 +0000 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2024-05-08 17:47:01 +0000 |
| commit | 724bab150541efefa4b3ea27bf3d4a064e9fab8c (patch) | |
| tree | a15bfa520133d4d038013bd4ee285f1bcbb4608a /src/runtime/trace.go | |
| parent | 11047345f53fb1484e76fd59d6e044c219d204e5 (diff) | |
| download | go-724bab150541efefa4b3ea27bf3d4a064e9fab8c.tar.xz | |
runtime: add traceallocfree GODEBUG for alloc/free events in traces
This change adds expensive alloc/free events to traces, guarded by a
GODEBUG that can be set at run time by mutating the GODEBUG environment
variable. This supersedes the alloc/free trace deleted in a previous CL.
There are two parts to this CL.
The first part is adding a mechanism for exposing experimental events
through the tracer and trace parser. This boils down to a new
ExperimentalEvent event type in the parser API which simply reveals the
raw event data for the event. Each experimental event can also be
associated with "experimental data" which is associated with a
particular generation. This experimental data is just exposed as a bag
of bytes that supplements the experimental events.
In the runtime, this CL organizes experimental events by experiment.
An experiment is defined by a set of experimental events and a single
special batch type. Batches of this special type are exposed through the
parser's API as the aforementioned "experimental data".
The second part of this CL is defining the AllocFree experiment, which
defines 9 new experimental events covering heap object alloc/frees, span
alloc/frees, and goroutine stack alloc/frees. It also generates special
batches that contain a type table: a mapping of IDs to type information.
Change-Id: I965c00e3dcfdf5570f365ff89d0f70d8aeca219c
Reviewed-on: https://go-review.googlesource.com/c/go/+/583377
Reviewed-by: Michael Pratt <mpratt@google.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/runtime/trace.go')
| -rw-r--r-- | src/runtime/trace.go | 37 |
1 files changed, 37 insertions, 0 deletions
diff --git a/src/runtime/trace.go b/src/runtime/trace.go index 6e0808ea76..49ac3e2d45 100644 --- a/src/runtime/trace.go +++ b/src/runtime/trace.go @@ -67,6 +67,7 @@ var trace struct { // There are 2 of each: one for gen%2, one for 1-gen%2. stackTab [2]traceStackTable // maps stack traces to unique ids stringTab [2]traceStringTable // maps strings to unique ids + typeTab [2]traceTypeTable // maps type pointers to unique ids // cpuLogRead accepts CPU profile samples from the signal handler where // they're generated. There are two profBufs here: one for gen%2, one for @@ -110,6 +111,10 @@ var trace struct { // as a publication barrier. enabled bool + // enabledWithAllocFree is set if debug.traceallocfree is != 0 when tracing begins. + // It follows the same synchronization protocol as enabled. + enabledWithAllocFree bool + // Trace generation counter. gen atomic.Uintptr lastNonZeroGen uintptr // last non-zero value of gen @@ -126,6 +131,12 @@ var trace struct { // // Mutated only during stop-the-world. seqGC uint64 + + // minPageHeapAddr is the minimum address of the page heap when tracing started. + minPageHeapAddr uint64 + + // debugMalloc is the value of debug.malloc before tracing began. + debugMalloc bool } // Trace public API. @@ -216,6 +227,10 @@ func StartTrace() error { // Prevent sysmon from running any code that could generate events. lock(&sched.sysmonlock) + // Grab the minimum page heap address. All Ps are stopped, so it's safe to read this since + // nothing can allocate heap memory. + trace.minPageHeapAddr = uint64(mheap_.pages.inUse.ranges[0].base.addr()) + // Reset mSyscallID on all Ps while we have them stationary and the trace is disabled. for _, pp := range allp { pp.trace.mSyscallID = -1 @@ -236,6 +251,12 @@ func StartTrace() error { // After trace.gen is updated, other Ms may start creating trace buffers and emitting // data into them. trace.enabled = true + if debug.traceallocfree.Load() != 0 { + // Enable memory events since the GODEBUG is set. + trace.debugMalloc = debug.malloc + trace.enabledWithAllocFree = true + debug.malloc = true + } trace.gen.Store(firstGen) // Wait for exitingSyscall to drain. @@ -267,6 +288,11 @@ func StartTrace() error { tl.GCActive() } + // Dump a snapshot of memory, if enabled. + if trace.enabledWithAllocFree { + traceSnapshotMemory() + } + // Record the heap goal so we have it at the very beginning of the trace. tl.HeapGoal() @@ -556,6 +582,7 @@ func traceAdvance(stopTrace bool) { // stacks may generate new strings. traceCPUFlush(gen) trace.stackTab[gen%2].dump(gen) + trace.typeTab[gen%2].dump(gen) trace.stringTab[gen%2].reset(gen) // That's it. This generation is done producing buffers. @@ -585,6 +612,16 @@ func traceAdvance(stopTrace bool) { // Finish off CPU profile reading. traceStopReadCPU() + + // Reset debug.malloc if necessary. Note that this is set in a racy + // way; that's OK. Some mallocs may still enter into the debug.malloc + // block, but they won't generate events because tracing is disabled. + // That is, it's OK if mallocs read a stale debug.malloc or + // trace.enabledWithAllocFree value. + if trace.enabledWithAllocFree { + trace.enabledWithAllocFree = false + debug.malloc = trace.debugMalloc + } } else { // Go over each P and emit a status event for it if necessary. // |
