gocore: expand bigslice test to test deconstructed values on the stack

Change-Id: Ie525b23748a53fffd9876515ebf2caa93744f523
Reviewed-on: https://go-review.googlesource.com/c/debug/+/659796
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 ffb9bd4..6899f78 100644
--- a/internal/gocore/gocore_test.go
+++ b/internal/gocore/gocore_test.go
@@ -296,13 +296,15 @@
 						}
 						return true
 					})
-					if largeObjects != 1 {
-						t.Errorf("expected exactly one object larger than %d, found %d", largeObjectThreshold, largeObjects)
+					if largeObjects != 3 {
+						t.Errorf("expected exactly three object larger than %d, found %d", largeObjectThreshold, largeObjects)
 					}
 
 					// Check object counts.
+					//
+					// TODO(mknyszek): Support typing roots in pieces.
 					if want := 32 << 10; bigSliceElemObjects != want {
-						t.Errorf("expected exactly %d main.globalBigSliceInt objects, found %d", want, bigSliceElemObjects)
+						t.Errorf("expected exactly %d main.bigSliceElem objects, found %d", want, bigSliceElemObjects)
 					}
 				})
 				t.Run("large.go", func(t *testing.T) {
diff --git a/internal/gocore/testdata/testprogs/bigslice.go b/internal/gocore/testdata/testprogs/bigslice.go
index d383fea..322913f 100644
--- a/internal/gocore/testdata/testprogs/bigslice.go
+++ b/internal/gocore/testdata/testprogs/bigslice.go
@@ -10,6 +10,7 @@
 
 import (
 	"os"
+	"runtime"
 
 	"golang.org/x/debug/internal/testenv"
 )
@@ -19,13 +20,42 @@
 }
 
 var globalBigSlice []*bigSliceElem
+var block chan struct{}
 
 func main() {
 	testenv.RunThenCrash(os.Getenv("GO_DEBUG_TEST_COREDUMP_FILTER"), func() any {
-		globalBigSlice = make([]*bigSliceElem, 32<<10)
-		for i := range globalBigSlice {
-			globalBigSlice[i] = &bigSliceElem{float64(i), float64(i) - 0.5, float64(i * 124)}
-		}
+		globalBigSlice = *makeBigSlice()
+		ready := make(chan []*bigSliceElem)
+		go func() {
+			// This funny incantation exists to force the bs0 and bs1 slice
+			// headers to be deconstructed by the compiler and stored in pieces
+			// on the stack. This tests whether gocore can piece it back
+			// together.
+			bsp0 := makeBigSlice()
+			bsp1 := makeBigSlice()
+			bs0 := *bsp0
+			bs1 := *bsp1
+			runtime.GC()
+			ready <- bs0
+			ready <- bs1
+			runtime.KeepAlive(bs0)
+			runtime.KeepAlive(bs1)
+		}()
+		<-ready
+
 		return nil
 	})
 }
+
+// This function signature looks weird, returning a pointer to a slice, but it's
+// to try and force deconstruction of the slice value by the compiler in the caller.
+// See callers of makeBigSlice.
+//
+//go:noinline
+func makeBigSlice() *[]*bigSliceElem {
+	bs := make([]*bigSliceElem, 32<<10)
+	for i := range bs {
+		bs[i] = &bigSliceElem{float64(i), float64(i) - 0.5, float64(i * 124)}
+	}
+	return &bs
+}