internal/gocore: fix large types dereference (Go 1.22+)
viewcore supports Go 1.22+ allocation headers since CL 608475. The CL
description mentions:
> This gets the goroot test passing again, which is a low bar... but
> it's something.
This CL fixes an untested path, and modifies test.go to hit that path by
allocating a "large" object (>32760 bytes). When allocating a large
object, viewcore panics:
```
panic: asking for field of non-struct [recovered]
panic: asking for field of non-struct
goroutine 170 [running]:
testing.tRunner.func1.2({0x72ce80, 0x7b86c0})
/usr/lib/google-golang/src/testing/testing.go:1632 +0x230
testing.tRunner.func1()
/usr/lib/google-golang/src/testing/testing.go:1635 +0x35e
panic({0x72ce80?, 0x7b86c0?})
/usr/lib/google-golang/src/runtime/panic.go:785 +0x132
golang.org/x/debug/internal/gocore.(*Type).field(...)
/usr/local/google/home/aktau/gob/go/debug/internal/gocore/type.go:85
golang.org/x/debug/internal/gocore.region.Field(...)
/usr/local/google/home/aktau/gob/go/debug/internal/gocore/region.go:163
golang.org/x/debug/internal/gocore.(*Process).readSpans(0xc0001fe000, {0xc0001fe000?, 0xc000000f50?, 0xc000000500?}, {0xc000aa0480?, 0x1, 0xc0005d7260?})
/usr/local/google/home/aktau/gob/go/debug/internal/gocore/process.go:596 +0x5125
golang.org/x/debug/internal/gocore.(*Process).readHeap(0xc0001fe000)
/usr/local/google/home/aktau/gob/go/debug/internal/gocore/process.go:254 +0x569
golang.org/x/debug/internal/gocore.Core(0xc0001f4000)
```
Indeed, `largeType` is not a struct, but a pointer to a struct:
```
// src/runtime/mheap.go
type mspan struct {
_ sys.NotInHeap
// ...
largeType *_type // malloc header for large objects.
}
```
Add a `Deref()` operation to fix this. Afterwards, the test does not panic
anymore. Though no attempt has been made to verify that the results are good.
Change-Id: I3569e5e1fba14a311507944f75e2ee2d27616a15
Reviewed-on: https://go-review.googlesource.com/c/debug/+/619875
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Nicolas Hillegeer <aktau@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
diff --git a/internal/gocore/process.go b/internal/gocore/process.go
index 1cd1431..e934358 100644
--- a/internal/gocore/process.go
+++ b/internal/gocore/process.go
@@ -592,7 +592,7 @@
// is not valid if the object is dead. However, because large objects are
// 1:1 with spans, we can be certain largeType is valid as long as the span
// is in use.
- typ := s.Field("largeType")
+ typ := s.Field("largeType").Deref()
nptrs := int64(typ.Field("PtrBytes").Uintptr()) / ptrSize
if typ.Field("Kind_").Uint8()&uint8(p.rtConstants["kindGCProg"]) != 0 {
panic("large object's GCProg was not unrolled")
diff --git a/internal/gocore/testdata/coretest/test.go b/internal/gocore/testdata/coretest/test.go
index ee0bb30..d3ebaf9 100644
--- a/internal/gocore/testdata/coretest/test.go
+++ b/internal/gocore/testdata/coretest/test.go
@@ -1,5 +1,22 @@
package main
+// Large is an object that (since Go 1.22) is allocated in a span that has a
+// non-nil largeType field. Meaning it must be (>maxSmallSize-mallocHeaderSize).
+// At the time of writing this is (32768 - 8).
+type Large struct {
+ ptr *uint8 // Object must contain a pointer to trigger code path.
+ arr [32768 - 8]uint8
+}
+
+func ping(o *Large) {
+ o.ptr = &o.arr[5]
+ o.arr[5] = 0xCA
+}
+
func main() {
+ var o Large
+ go ping(&o) // Force an escape of o.
+ o.arr[14] = 0xDE // Prevent a future smart compiler from allocating o directly on pings stack.
+
_ = *(*int)(nil)
}