internal/gocore: add sanity checking to TestVersions

This strengthens the guarantees about what viewcore(1) supports.
Specifically, we have binaries built with PIE which appear to load
properly (`Core(...)` doesn't return an error, but return (e.g.) zero
goroutines. Tests should fail if this happens, there's always at least
one goroutine.

The sanity checking functions are defined separately so we can re-use
them in a test extension patch where we supply several types of binaries
built with non-standard flags (and without the `go build` tool).

Change-Id: I492724a7a9d371ce694829368c7dfb73066bb821
Reviewed-on: https://go-review.googlesource.com/c/debug/+/618976
Reviewed-by: Nicolas Hillegeer <aktau@google.com>
Auto-Submit: Nicolas Hillegeer <aktau@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Nicolas Hillegeer <aktau@google.com>
diff --git a/internal/gocore/dominator_test.go b/internal/gocore/dominator_test.go
index 79404f3..699169e 100644
--- a/internal/gocore/dominator_test.go
+++ b/internal/gocore/dominator_test.go
@@ -15,7 +15,7 @@
 func TestLT(t *testing.T) {
 	p := loadExample(t)
 	lt := runLT(p)
-	sanityCheck(t, lt)
+	checkDominator(t, lt)
 	if false {
 		lt.dot(os.Stdout)
 	}
@@ -29,7 +29,7 @@
 	}
 }
 
-func sanityCheck(t *testing.T, d ltDom) bool {
+func checkDominator(t *testing.T, d ltDom) bool {
 	t.Helper()
 	// Build pointer-y graph.
 	pRoot := sanityVertex{}
diff --git a/internal/gocore/gocore_test.go b/internal/gocore/gocore_test.go
index 90889ef..91f69c1 100644
--- a/internal/gocore/gocore_test.go
+++ b/internal/gocore/gocore_test.go
@@ -390,6 +390,33 @@
 	}
 }
 
+// getStat returns the first (depth first) stat in the hierarchy which matches
+// name, nil otherwise.
+func getStat(stat *Stats, name string) *Stats {
+	if stat.Name == name {
+		return stat
+	}
+	for _, child := range stat.Children {
+		if found := getStat(child, name); found != nil {
+			return found
+		}
+	}
+	return nil
+}
+
+func checkProcess(t *testing.T, p *Process) {
+	t.Helper()
+	if gs := p.Goroutines(); len(gs) == 0 {
+		t.Error("len(p.Goroutines()) == 0, want >0")
+	}
+
+	const heapName = "heap"
+	heapStat := getStat(p.Stats(), heapName)
+	if heapStat == nil || heapStat.Size == 0 {
+		t.Errorf("stat[%q].Size == 0, want >0", heapName)
+	}
+}
+
 func TestVersions(t *testing.T) {
 	versions := []string{
 		"1.10",
@@ -405,12 +432,27 @@
 	}
 	for _, ver := range versions {
 		t.Run(ver, func(t *testing.T) {
-			loadExampleVersion(t, ver)
+			p := loadExampleVersion(t, ver)
+			checkProcess(t, p)
+
+			lt := runLT(p)
+			if !checkDominator(t, lt) {
+				t.Errorf("sanityCheckDominator(...) = false, want true")
+			}
 		})
 	}
 
 	t.Run("goroot", func(t *testing.T) {
-		loadExampleGenerated(t)
+		p := loadExampleGenerated(t)
+		checkProcess(t, p)
+
+		// TODO(aktau): Move sanityCheckDominator into sanityCheckProcess once this
+		//              passes for loadExampleGenerated.
+		t.Skip(`skipping dominator check due to "panic: can't find type runtime.itab"`)
+		lt := runLT(p)
+		if !checkDominator(t, lt) {
+			t.Errorf("sanityCheckDominator(...) = false, want true")
+		}
 	})
 }