1// Copyright 2024 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 for memory events. 6 7package runtime 8 9import ( 10 "internal/abi" 11 "runtime/internal/sys" 12) 13 14// Batch type values for the alloc/free experiment. 15const ( 16 traceAllocFreeTypesBatch = iota // Contains types. [{id, address, size, ptrspan, name length, name string} ...] 17 traceAllocFreeInfoBatch // Contains info for interpreting events. [min heap addr, page size, min heap align, min stack align] 18) 19 20// traceSnapshotMemory takes a snapshot of all runtime memory that there are events for 21// (heap spans, heap objects, goroutine stacks, etc.) and writes out events for them. 22// 23// The world must be stopped and tracing must be enabled when this function is called. 24func traceSnapshotMemory(gen uintptr) { 25 assertWorldStopped() 26 27 // Write a batch containing information that'll be necessary to 28 // interpret the events. 29 var flushed bool 30 w := unsafeTraceExpWriter(gen, nil, traceExperimentAllocFree) 31 w, flushed = w.ensure(1 + 4*traceBytesPerNumber) 32 if flushed { 33 // Annotate the batch as containing additional info. 34 w.byte(byte(traceAllocFreeInfoBatch)) 35 } 36 37 // Emit info. 38 w.varint(uint64(trace.minPageHeapAddr)) 39 w.varint(uint64(pageSize)) 40 w.varint(uint64(minHeapAlign)) 41 w.varint(uint64(fixedStack)) 42 43 // Finish writing the batch. 44 w.flush().end() 45 46 // Start tracing. 47 trace := traceAcquire() 48 if !trace.ok() { 49 throw("traceSnapshotMemory: tracing is not enabled") 50 } 51 52 // Write out all the heap spans and heap objects. 53 for _, s := range mheap_.allspans { 54 if s.state.get() == mSpanDead { 55 continue 56 } 57 // It's some kind of span, so trace that it exists. 58 trace.SpanExists(s) 59 60 // Write out allocated objects if it's a heap span. 61 if s.state.get() != mSpanInUse { 62 continue 63 } 64 65 // Find all allocated objects. 66 abits := s.allocBitsForIndex(0) 67 for i := uintptr(0); i < uintptr(s.nelems); i++ { 68 if abits.index < uintptr(s.freeindex) || abits.isMarked() { 69 x := s.base() + i*s.elemsize 70 trace.HeapObjectExists(x, s.typePointersOfUnchecked(x).typ) 71 } 72 abits.advance() 73 } 74 } 75 76 // Write out all the goroutine stacks. 77 forEachGRace(func(gp *g) { 78 trace.GoroutineStackExists(gp.stack.lo, gp.stack.hi-gp.stack.lo) 79 }) 80 traceRelease(trace) 81} 82 83func traceSpanTypeAndClass(s *mspan) traceArg { 84 if s.state.get() == mSpanInUse { 85 return traceArg(s.spanclass) << 1 86 } 87 return traceArg(1) 88} 89 90// SpanExists records an event indicating that the span exists. 91func (tl traceLocker) SpanExists(s *mspan) { 92 tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvSpan, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s)) 93} 94 95// SpanAlloc records an event indicating that the span has just been allocated. 96func (tl traceLocker) SpanAlloc(s *mspan) { 97 tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvSpanAlloc, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s)) 98} 99 100// SpanFree records an event indicating that the span is about to be freed. 101func (tl traceLocker) SpanFree(s *mspan) { 102 tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvSpanFree, traceSpanID(s)) 103} 104 105// traceSpanID creates a trace ID for the span s for the trace. 106func traceSpanID(s *mspan) traceArg { 107 return traceArg(uint64(s.base())-trace.minPageHeapAddr) / pageSize 108} 109 110// HeapObjectExists records that an object already exists at addr with the provided type. 111// The type is optional, and the size of the slot occupied the object is inferred from the 112// span containing it. 113func (tl traceLocker) HeapObjectExists(addr uintptr, typ *abi.Type) { 114 tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvHeapObject, traceHeapObjectID(addr), tl.rtype(typ)) 115} 116 117// HeapObjectAlloc records that an object was newly allocated at addr with the provided type. 118// The type is optional, and the size of the slot occupied the object is inferred from the 119// span containing it. 120func (tl traceLocker) HeapObjectAlloc(addr uintptr, typ *abi.Type) { 121 tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvHeapObjectAlloc, traceHeapObjectID(addr), tl.rtype(typ)) 122} 123 124// HeapObjectFree records that an object at addr is about to be freed. 125func (tl traceLocker) HeapObjectFree(addr uintptr) { 126 tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvHeapObjectFree, traceHeapObjectID(addr)) 127} 128 129// traceHeapObjectID creates a trace ID for a heap object at address addr. 130func traceHeapObjectID(addr uintptr) traceArg { 131 return traceArg(uint64(addr)-trace.minPageHeapAddr) / minHeapAlign 132} 133 134// GoroutineStackExists records that a goroutine stack already exists at address base with the provided size. 135func (tl traceLocker) GoroutineStackExists(base, size uintptr) { 136 order := traceCompressStackSize(size) 137 tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGoroutineStack, traceGoroutineStackID(base), order) 138} 139 140// GoroutineStackAlloc records that a goroutine stack was newly allocated at address base with the provided size.. 141func (tl traceLocker) GoroutineStackAlloc(base, size uintptr) { 142 order := traceCompressStackSize(size) 143 tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGoroutineStackAlloc, traceGoroutineStackID(base), order) 144} 145 146// GoroutineStackFree records that a goroutine stack at address base is about to be freed. 147func (tl traceLocker) GoroutineStackFree(base uintptr) { 148 tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGoroutineStackFree, traceGoroutineStackID(base)) 149} 150 151// traceGoroutineStackID creates a trace ID for the goroutine stack from its base address. 152func traceGoroutineStackID(base uintptr) traceArg { 153 return traceArg(uint64(base)-trace.minPageHeapAddr) / fixedStack 154} 155 156// traceCompressStackSize assumes size is a power of 2 and returns log2(size). 157func traceCompressStackSize(size uintptr) traceArg { 158 if size&(size-1) != 0 { 159 throw("goroutine stack size is not a power of 2") 160 } 161 return traceArg(sys.Len64(uint64(size))) 162} 163