// runtime/mprof.go
var ( mbuckets atomic.UnsafePointer // *bucket, memory profile buckets ...
)
// runtime/mprof.go
var ( mbuckets atomic.UnsafePointer // *bucket, memory profile buckets ...
)
// runtime/mprof.go
var ( mbuckets atomic.UnsafePointer // *bucket, memory profile buckets ...
)
func findSymbolValue(ef *elf.File, sectionName, symName string) (uint64, error) { section := ef.Section(sectionName) strtab := ef.Sections[section.Link] symReader := section.Open() entry := make([]byte, 24) // Elf64_Sym target := []byte(symName) nameBuf := make([]byte, len(target)+1) for { if _, err := symReader.Read(entry); err != nil { break } nameIdx := ef.ByteOrder.Uint32(entry[0:4]) value := ef.ByteOrder.Uint64(entry[8:16]) n, _ := strtab.ReadAt(nameBuf, int64(nameIdx)) if n > len(target) && nameBuf[len(target)] != 0 { continue } if string(nameBuf[:len(target)]) == symName { return value, nil } } return 0, fmt.Errorf("%s not found", symName)
}
func findSymbolValue(ef *elf.File, sectionName, symName string) (uint64, error) { section := ef.Section(sectionName) strtab := ef.Sections[section.Link] symReader := section.Open() entry := make([]byte, 24) // Elf64_Sym target := []byte(symName) nameBuf := make([]byte, len(target)+1) for { if _, err := symReader.Read(entry); err != nil { break } nameIdx := ef.ByteOrder.Uint32(entry[0:4]) value := ef.ByteOrder.Uint64(entry[8:16]) n, _ := strtab.ReadAt(nameBuf, int64(nameIdx)) if n > len(target) && nameBuf[len(target)] != 0 { continue } if string(nameBuf[:len(target)]) == symName { return value, nil } } return 0, fmt.Errorf("%s not found", symName)
}
func findSymbolValue(ef *elf.File, sectionName, symName string) (uint64, error) { section := ef.Section(sectionName) strtab := ef.Sections[section.Link] symReader := section.Open() entry := make([]byte, 24) // Elf64_Sym target := []byte(symName) nameBuf := make([]byte, len(target)+1) for { if _, err := symReader.Read(entry); err != nil { break } nameIdx := ef.ByteOrder.Uint32(entry[0:4]) value := ef.ByteOrder.Uint64(entry[8:16]) n, _ := strtab.ReadAt(nameBuf, int64(nameIdx)) if n > len(target) && nameBuf[len(target)] != 0 { continue } if string(nameBuf[:len(target)]) == symName { return value, nil } } return 0, fmt.Errorf("%s not found", symName)
}
type bucket struct { _ sys.NotInHeap next *bucket allnext *bucket typ bucketType hash uintptr size uintptr nstk uintptr
}
type bucket struct { _ sys.NotInHeap next *bucket allnext *bucket typ bucketType hash uintptr size uintptr nstk uintptr
}
type bucket struct { _ sys.NotInHeap next *bucket allnext *bucket typ bucketType hash uintptr size uintptr nstk uintptr
}
--go-heap-profiler=disabled # off
--go-heap-profiler=enabled # default, passive only
--go-heap-profiler=force # write MemProfileRate if zero
--go-heap-profiler=disabled # off
--go-heap-profiler=enabled # default, passive only
--go-heap-profiler=force # write MemProfileRate if zero
--go-heap-profiler=disabled # off
--go-heap-profiler=enabled # default, passive only
--go-heap-profiler=force # write MemProfileRate if zero
container_go_alloc_bytes_total # total bytes allocated
container_go_alloc_objects_total # total objects allocated
container_go_alloc_bytes_total # total bytes allocated
container_go_alloc_objects_total # total objects allocated
container_go_alloc_bytes_total # total bytes allocated
container_go_alloc_objects_total # total objects allocated - Find the virtual address of runtime.mbuckets in the Go binary's symbol table.
- Read the pointer value at that address from /proc/<pid>/mem.
- Walk the linked list, reading each bucket's header, stack PCs, and memRecord.
- Convert to pprof format and upload. - Stripped binaries are skipped. No .symtab, no runtime.mbuckets address, nothing we can do externally.
- The active cycle updates on GC. Between GCs, new allocations go into future[0..2] and we don't see them. Same limitation as runtime.MemProfile().
- Go-internal struct layout. If the bucket struct changes in a future Go release, we'll need to update. The layout has been stable since Go 1.17, but there's no API guarantee.
- Goroutine, block, and mutex profiles are not yet exposed. Block and mutex use the same infrastructure (bbuckets, xbuckets), but both are disabled by default and have real overhead if enabled (checks on every mutex/channel op), so we're not force-enabling them. - Marshals and unmarshals the product 10 times in a row.
- Builds a "search index" by lowercasing, uppercasing and title-casing every word and generating every 2 to 4 character n-grams.
- Builds 20 nested "related products" maps, each with three sub-maps.
- Marshals and unmarshals the whole result one more time "for caching".