internal/gocore: refactor heapArena reading
There are a lot of different historical changes to the heap arena
structure, making Process.readHeap difficult to follow. Refactor to
split out version-specific logic to individual methods.
Reading allocation headers is not yet supported by this CL.
For golang/go#64049.
Change-Id: Ia4198bd8492d8426c7a2cc8671b78c49ab6b7161
Reviewed-on: https://go-review.googlesource.com/c/debug/+/541131
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/internal/gocore/process.go b/internal/gocore/process.go
index 7c2ed8f..52853d5 100644
--- a/internal/gocore/process.go
+++ b/internal/gocore/process.go
@@ -177,10 +177,12 @@
return p, nil
}
+// arena is a summary of the size of components of a heapArena.
type arena struct {
heapMin core.Address
heapMax core.Address
+ // Optional.
bitmapMin core.Address
bitmapMax core.Address
@@ -199,37 +201,13 @@
func (p *Process) readHeap() {
ptrSize := p.proc.PtrSize()
- logPtrSize := p.proc.LogPtrSize()
p.pageTable = map[core.Address]*pageTableEntry{}
mheap := p.rtGlobals["mheap_"]
var arenas []arena
if mheap.HasField("spans") {
// go 1.9 or 1.10. There is a single arena.
- arenaStart := core.Address(mheap.Field("arena_start").Uintptr())
- arenaUsed := core.Address(mheap.Field("arena_used").Uintptr())
- arenaEnd := core.Address(mheap.Field("arena_end").Uintptr())
- bitmapEnd := core.Address(mheap.Field("bitmap").Uintptr())
- bitmapStart := bitmapEnd.Add(-int64(mheap.Field("bitmap_mapped").Uintptr()))
- spanTableStart := mheap.Field("spans").SlicePtr().Address()
- spanTableEnd := spanTableStart.Add(mheap.Field("spans").SliceCap() * ptrSize)
- arenas = append(arenas, arena{
- heapMin: arenaStart,
- heapMax: arenaEnd,
- bitmapMin: bitmapStart,
- bitmapMax: bitmapEnd,
- spanTableMin: spanTableStart,
- spanTableMax: spanTableEnd,
- })
-
- // Copy pointer bits to heap info.
- // Note that the pointer bits are stored backwards.
- for a := arenaStart; a < arenaUsed; a = a.Add(ptrSize) {
- off := a.Sub(arenaStart) >> logPtrSize
- if p.proc.ReadUint8(bitmapEnd.Add(-(off>>2)-1))>>uint(off&3)&1 != 0 {
- p.setHeapPtr(a)
- }
- }
+ arenas = append(arenas, p.readArena19(mheap))
} else {
// go 1.11+. Has multiple arenas.
arenaSize := p.rtConstants["heapArenaBytes"]
@@ -240,6 +218,7 @@
if ptrSize == 4 && arenaBaseOffset != 0 {
panic("arenaBaseOffset must be 0 for 32-bit inferior")
}
+
level1Table := mheap.Field("arenas")
level1size := level1Table.ArrayLen()
for level1 := int64(0); level1 < level1size; level1++ {
@@ -258,54 +237,8 @@
min := core.Address(arenaSize*(level2+level1*level2size) - arenaBaseOffset)
max := min.Add(arenaSize)
- var bitmap region
- var oneBitBitmap bool
- if a.HasField("heapArenaPtrScalar") { // go 1.22
- bitmap = a.Field("heapArenaPtrScalar").Field("bitmap")
- oneBitBitmap = true
- } else {
- bitmap = a.Field("bitmap")
- oneBitBitmap = a.HasField("noMorePtrs") // Starting in 1.20.
- }
- spans := a.Field("spans")
- arenas = append(arenas, arena{
- heapMin: min,
- heapMax: max,
- bitmapMin: bitmap.a,
- bitmapMax: bitmap.a.Add(bitmap.ArrayLen()),
- spanTableMin: spans.a,
- spanTableMax: spans.a.Add(spans.ArrayLen() * ptrSize),
- })
-
- // Copy out ptr/nonptr bits
- n := bitmap.ArrayLen()
- for i := int64(0); i < n; i++ {
- if oneBitBitmap {
- // The array uses 1 bit per word of heap. See mbitmap.go for
- // more information.
- m := bitmap.ArrayIndex(i).Uintptr()
- bits := 8 * ptrSize
- for j := int64(0); j < bits; j++ {
- if m>>uint(j)&1 != 0 {
- p.setHeapPtr(min.Add((i*bits + j) * ptrSize))
- }
- }
- } else {
- // The nth byte is composed of 4 object bits and 4 live/dead
- // bits. We ignore the 4 live/dead bits, which are on the
- // high order side of the byte.
- //
- // See mbitmap.go for more information on the format of
- // the bitmap field of heapArena.
- m := bitmap.ArrayIndex(i).Uint8()
- for j := int64(0); j < 4; j++ {
- if m>>uint(j)&1 != 0 {
- p.setHeapPtr(min.Add((i*4 + j) * ptrSize))
- }
- }
- }
- }
+ arenas = append(arenas, p.readArena(a, min, max))
}
}
}
@@ -313,6 +246,112 @@
p.readSpans(mheap, arenas)
}
+// Read the global arena. Go 1.9 or 1.10 only, which has a single arena. Record
+// heap pointers and return the arena size summary.
+func (p *Process) readArena19(mheap region) arena {
+ ptrSize := p.proc.PtrSize()
+ logPtrSize := p.proc.LogPtrSize()
+
+ arenaStart := core.Address(mheap.Field("arena_start").Uintptr())
+ arenaUsed := core.Address(mheap.Field("arena_used").Uintptr())
+ arenaEnd := core.Address(mheap.Field("arena_end").Uintptr())
+ bitmapEnd := core.Address(mheap.Field("bitmap").Uintptr())
+ bitmapStart := bitmapEnd.Add(-int64(mheap.Field("bitmap_mapped").Uintptr()))
+ spanTableStart := mheap.Field("spans").SlicePtr().Address()
+ spanTableEnd := spanTableStart.Add(mheap.Field("spans").SliceCap() * ptrSize)
+
+ // Copy pointer bits to heap info.
+ // Note that the pointer bits are stored backwards.
+ for a := arenaStart; a < arenaUsed; a = a.Add(ptrSize) {
+ off := a.Sub(arenaStart) >> logPtrSize
+ if p.proc.ReadUint8(bitmapEnd.Add(-(off>>2)-1))>>uint(off&3)&1 != 0 {
+ p.setHeapPtr(a)
+ }
+ }
+
+ return arena{
+ heapMin: arenaStart,
+ heapMax: arenaEnd,
+ bitmapMin: bitmapStart,
+ bitmapMax: bitmapEnd,
+ spanTableMin: spanTableStart,
+ spanTableMax: spanTableEnd,
+ }
+}
+
+// Read a single heapArena. Go 1.11+, which has multiple areans. Record heap
+// pointers and return the arena size summary.
+func (p *Process) readArena(a region, min, max core.Address) arena {
+ ptrSize := p.proc.PtrSize()
+
+ var bitmap region
+ if a.HasField("bitmap") { // Before go 1.22
+ bitmap = a.Field("bitmap")
+ if oneBitBitmap := a.HasField("noMorePtrs"); oneBitBitmap { // Starting in go 1.20
+ p.readOneBitBitmap(bitmap, min)
+ } else {
+ p.readMultiBitBitmap(bitmap, min)
+ }
+ } else if a.HasField("heapArenaPtrScalar") && a.Field("heapArenaPtrScalar").HasField("bitmap") { // go 1.22 without allocation headers
+ // TODO: This configuration only existed between CL 537978 and CL
+ // 538217 and was never released. Prune support.
+ bitmap = a.Field("heapArenaPtrScalar").Field("bitmap")
+ p.readOneBitBitmap(bitmap, min)
+ } else { // go 1.22 with allocation headers
+ panic("unimplemented")
+ }
+
+ spans := a.Field("spans")
+ arena := arena{
+ heapMin: min,
+ heapMax: max,
+ spanTableMin: spans.a,
+ spanTableMax: spans.a.Add(spans.ArrayLen() * ptrSize),
+ }
+ if bitmap.a != 0 {
+ arena.bitmapMin = bitmap.a
+ arena.bitmapMax = bitmap.a.Add(bitmap.ArrayLen())
+ }
+ return arena
+}
+
+// Read a one-bit bitmap (Go 1.20+), recording the heap pointers.
+func (p *Process) readOneBitBitmap(bitmap region, min core.Address) {
+ ptrSize := p.proc.PtrSize()
+ n := bitmap.ArrayLen()
+ for i := int64(0); i < n; i++ {
+ // The array uses 1 bit per word of heap. See mbitmap.go for
+ // more information.
+ m := bitmap.ArrayIndex(i).Uintptr()
+ bits := 8 * ptrSize
+ for j := int64(0); j < bits; j++ {
+ if m>>uint(j)&1 != 0 {
+ p.setHeapPtr(min.Add((i*bits + j) * ptrSize))
+ }
+ }
+ }
+}
+
+// Read a multi-bit bitmap (Go 1.11-1.20), recording the heap pointers.
+func (p *Process) readMultiBitBitmap(bitmap region, min core.Address) {
+ ptrSize := p.proc.PtrSize()
+ n := bitmap.ArrayLen()
+ for i := int64(0); i < n; i++ {
+ // The nth byte is composed of 4 object bits and 4 live/dead
+ // bits. We ignore the 4 live/dead bits, which are on the
+ // high order side of the byte.
+ //
+ // See mbitmap.go for more information on the format of
+ // the bitmap field of heapArena.
+ m := bitmap.ArrayIndex(i).Uint8()
+ for j := int64(0); j < 4; j++ {
+ if m>>uint(j)&1 != 0 {
+ p.setHeapPtr(min.Add((i*4 + j) * ptrSize))
+ }
+ }
+ }
+}
+
func (p *Process) readSpans(mheap region, arenas []arena) {
var all int64
var text int64