Tech News
← Back to articles

Go allocation probe

read original related products more articles

Published 2025-07-18

The profiling tools in go will tell you where allocations happened in the code, but won't record the type of the allocation. I suspected that in the code I was looking at, some specific types were causing a lot of allocation but that those allocations were spread over many locations. So with the help of Matt Knight I wrote perhaps the most appallingly fragile code I have ever written - go_allocation_probe.

Every heap allocation in go afaict goes through a single function - mallocgc.

func mallocgc ( size uintptr , typ * _type , needzero bool ) unsafe . Pointer

So we attach a bpftrace uprobe to mallocgc. The go calling convention on x64 is pretty straightforward, so we can easily grab the size and typ arguments from registers, and we can count the number of calls and total bytes per type. Easy.

Now we can just print out the name of the type and... what a minute... what is this *_type ?

Turns out it's an alias for abi.Type.

type Type struct { Size_ uintptr PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers Hash uint32 // hash of type; avoids computation in hash tables TFlag TFlag // extra type information flags Align_ uint8 // alignment of variable with this type FieldAlign_ uint8 // alignment of struct field with this type Kind_ Kind // enumeration for C // function for comparing objects of this type // (ptr to object A, ptr to object B) -> ==? Equal func (unsafe . Pointer , unsafe . Pointer ) bool // GCData stores the GC type data for the garbage collector. // Normally, GCData points to a bitmask that describes the // ptr/nonptr fields of the type. The bitmask will have at // least PtrBytes/ptrSize bits. // If the TFlagGCMaskOnDemand bit is set, GCData is instead a // **byte and the pointer to the bitmask is one dereference away. // The runtime will build the bitmask if needed. // (See runtime/type.go:getGCMask.) // Note: multiple types may have the same value of GCData, // including when TFlagGCMaskOnDemand is set. The types will, of course, // have the same pointer layout (but not necessarily the same size). GCData * byte Str NameOff // string form PtrToThis TypeOff // type for pointer to this type, may be zero }

So Str is the name of the type? Nope, it's an integer offset into... something.

// NameOff is the offset to a name from moduledata.types. See resolveNameOff in runtime. type NameOff int32

... continue reading