gocore: fix bad processing of large object pointers

Currently large object pointer finding has a bug in that we don't
interpret largeType as an array element type (which it is) and instead
as the single type for the entire array. This means we miss lots of
pointers in large objects that are, for example, a slice of pointers.

Change-Id: I9ade197b5db9ac52b829524e0d70822c8fdc1dbf
Reviewed-on: https://go-review.googlesource.com/c/debug/+/659335
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Nicolas Hillegeer <aktau@google.com>
diff --git a/internal/gocore/gocore_test.go b/internal/gocore/gocore_test.go
index 081db20..4ab0704 100644
--- a/internal/gocore/gocore_test.go
+++ b/internal/gocore/gocore_test.go
@@ -276,6 +276,7 @@
 				myPairObjects := 0
 				anyNodeObjects := 0
 				typeSafeNodeObjects := 0
+				bigSliceElemObjects := 0
 
 				p.ForEachObject(func(x Object) bool {
 					siz := p.Size(x)
@@ -291,6 +292,8 @@
 						anyNodeObjects++
 					case "main.typeSafeNode[main.myPair]":
 						typeSafeNodeObjects++
+					case "main.bigSliceElem":
+						bigSliceElemObjects++
 					}
 					n++
 					return true
@@ -298,7 +301,7 @@
 				if n < 10 {
 					t.Errorf("#objects = %d, want >10", n)
 				}
-				if largeObjects != 1 {
+				if largeObjects != 2 {
 					t.Errorf("expected exactly one object larger than %d, found %d", largeObjectThreshold, largeObjects)
 				}
 
@@ -316,6 +319,9 @@
 				if want := tsTrees * nodes; typeSafeNodeObjects != want {
 					t.Errorf("expected exactly %d main.typeSafeNode[main.myPair] objects, found %d", want, typeSafeNodeObjects)
 				}
+				if want := 32 << 10; bigSliceElemObjects != want {
+					t.Errorf("expected exactly %d main.globalBigSliceInt objects, found %d", want, bigSliceElemObjects)
+				}
 			})
 		}
 	})
diff --git a/internal/gocore/process.go b/internal/gocore/process.go
index 926dc4b..5080b8c 100644
--- a/internal/gocore/process.go
+++ b/internal/gocore/process.go
@@ -535,10 +535,13 @@
 					if hasGCProgs && typ.Field("Kind_").Uint8()&uint8(kindGCProg) != 0 {
 						panic("large object's GCProg was not unrolled")
 					}
+					size := typ.Field("Size_").Uintptr()
 					gcdata := typ.Field("GCData").Address()
-					for i := int64(0); i < nptrs; i++ {
-						if p.proc.ReadUint8(gcdata.Add(i/8))>>uint(i%8)&1 != 0 {
-							heap.setIsPointer(min.Add(i * int64(heap.ptrSize)))
+					for s := min; s < max; s = s.Add(int64(size)) {
+						for i := int64(0); i < nptrs; i++ {
+							if (p.proc.ReadUint8(gcdata.Add(i/8))>>uint(i%8))&1 != 0 {
+								heap.setIsPointer(s.Add(i * int64(heap.ptrSize)))
+							}
 						}
 					}
 				}
diff --git a/internal/gocore/testdata/coretest/test.go b/internal/gocore/testdata/coretest/test.go
index d0e8443..d217112 100644
--- a/internal/gocore/testdata/coretest/test.go
+++ b/internal/gocore/testdata/coretest/test.go
@@ -122,10 +122,15 @@
 	X() int64
 }
 
+type bigSliceElem struct {
+	x, y, z float64
+}
+
 var globalAnyTree AnyTree
 var globalAnyTreeFM func() int
 var globalTypeSafeTree TypeSafeTree[myPair]
 var globalTypeSafeTreeFM func() int
+var globalBigSlice []*bigSliceElem
 
 var block = make(chan struct{})
 
@@ -143,6 +148,10 @@
 }
 
 func main() {
+	globalBigSlice = make([]*bigSliceElem, 32<<10)
+	for i := range globalBigSlice {
+		globalBigSlice[i] = &bigSliceElem{float64(i), float64(i) - 0.5, float64(i * 124)}
+	}
 	globalAnyTree.root = makeAnyTree(5)
 	globalTypeSafeTree.root = makeTypeSafeTree(5)