cmd/link/internal/ld: add dwarf testing helpers

Many tests build a program just to analyze it with dwtest.Examiner. Add
gobuildAndExamine, a helper that returns Examiner directly to reduce
duplication in these tests.

Many tests also lookup the DIE for a specific subprogram, which includes
several verification steps. Package those up in findSubprogramDIE.

Change-Id: I72202ba289ae8389b682be525ff7e6cfbfc00ff3
Reviewed-on: https://go-review.googlesource.com/c/go/+/458196
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Michael Pratt <mpratt@google.com>
Run-TryBot: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
index a11541f..e3a2109 100644
--- a/src/cmd/link/internal/ld/dwarf_test.go
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -135,6 +135,45 @@
 	return &builtFile{f, dst}
 }
 
+// Helper to build a snippet of source for examination with dwtest.Examiner.
+func gobuildAndExamine(t *testing.T, source string, gcflags string) (*dwarf.Data, *dwtest.Examiner) {
+	dir := t.TempDir()
+
+	f := gobuild(t, dir, source, gcflags)
+	defer f.Close()
+
+	d, err := f.DWARF()
+	if err != nil {
+		t.Fatalf("error reading DWARF in program %q: %v", source, err)
+	}
+
+	rdr := d.Reader()
+	ex := &dwtest.Examiner{}
+	if err := ex.Populate(rdr); err != nil {
+		t.Fatalf("error populating DWARF examiner for program %q: %v", source, err)
+	}
+
+	return d, ex
+}
+
+func findSubprogramDIE(t *testing.T, ex *dwtest.Examiner, sym string) *dwarf.Entry {
+	dies := ex.Named(sym)
+	if len(dies) == 0 {
+		t.Fatalf("unable to locate DIE for %s", sym)
+	}
+	if len(dies) != 1 {
+		t.Fatalf("more than one %s DIE: %+v", sym, dies)
+	}
+	die := dies[0]
+
+	// Vet the DIE.
+	if die.Tag != dwarf.TagSubprogram {
+		t.Fatalf("unexpected tag %v on %s DIE", die.Tag, sym)
+	}
+
+	return die
+}
+
 func TestEmbeddedStructMarker(t *testing.T) {
 	t.Parallel()
 	testenv.MustHaveGoBuild(t)
@@ -341,21 +380,7 @@
 	prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive)
 	const iLineOffset = 2
 
-	dir := t.TempDir()
-
-	f := gobuild(t, dir, prog, NoOpt)
-	defer f.Close()
-
-	d, err := f.DWARF()
-	if err != nil {
-		t.Fatalf("error reading DWARF: %v", err)
-	}
-
-	rdr := d.Reader()
-	ex := dwtest.Examiner{}
-	if err := ex.Populate(rdr); err != nil {
-		t.Fatalf("error reading DWARF: %v", err)
-	}
+	d, ex := gobuildAndExamine(t, prog, NoOpt)
 
 	// Locate the main.main DIE
 	mains := ex.Named("main.main")
@@ -466,44 +491,17 @@
     G = x
 }
 `
-	dir := t.TempDir()
-
 	// Note: this is a build with "-l=4", as opposed to "-l -N". The
 	// test is intended to verify DWARF that is only generated when
 	// the inliner is active. We're only going to look at the DWARF for
 	// main.main, however, hence we build with "-gcflags=-l=4" as opposed
 	// to "-gcflags=all=-l=4".
-	f := gobuild(t, dir, prog, OptInl4)
-	defer f.Close()
-
-	d, err := f.DWARF()
-	if err != nil {
-		t.Fatalf("error reading DWARF: %v", err)
-	}
+	d, ex := gobuildAndExamine(t, prog, OptInl4)
 
 	// The inlined subroutines we expect to visit
 	expectedInl := []string{"main.cand"}
 
-	rdr := d.Reader()
-	ex := dwtest.Examiner{}
-	if err := ex.Populate(rdr); err != nil {
-		t.Fatalf("error reading DWARF: %v", err)
-	}
-
-	// Locate the main.main DIE
-	mains := ex.Named("main.main")
-	if len(mains) == 0 {
-		t.Fatalf("unable to locate DIE for main.main")
-	}
-	if len(mains) != 1 {
-		t.Fatalf("more than one main.main DIE")
-	}
-	maindie := mains[0]
-
-	// Vet the main.main DIE
-	if maindie.Tag != dwarf.TagSubprogram {
-		t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
-	}
+	maindie := findSubprogramDIE(t, ex, "main.main")
 
 	// Walk main's children and pick out the inlined subroutines
 	mainIdx := ex.IdxFromOffset(maindie.Offset)
@@ -1334,20 +1332,12 @@
 		t.Fatalf("error parsing DWARF: %v", err)
 	}
 	rdr := dw.Reader()
-	ex := dwtest.Examiner{}
+	ex := &dwtest.Examiner{}
 	if err := ex.Populate(rdr); err != nil {
 		t.Fatalf("error reading DWARF: %v", err)
 	}
 
-	// Locate the main.main DIE
-	mains := ex.Named("main.main")
-	if len(mains) == 0 {
-		t.Fatalf("unable to locate DIE for main.main")
-	}
-	if len(mains) != 1 {
-		t.Fatalf("more than one main.main DIE")
-	}
-	maindie := mains[0]
+	maindie := findSubprogramDIE(t, ex, "main.main")
 
 	// Collect the start/end PC for main.main
 	lowpc := maindie.Val(dwarf.AttrLowpc).(uint64)
@@ -1556,38 +1546,12 @@
 	println(v1, v2, v3[0], v4, v5, v6)
 }
 `
-	dir := t.TempDir()
-	f := gobuild(t, dir, prog, NoOpt)
-	defer f.Close()
+	_, ex := gobuildAndExamine(t, prog, NoOpt)
 
-	d, err := f.DWARF()
-	if err != nil {
-		t.Fatalf("error reading DWARF: %v", err)
-	}
-
-	rdr := d.Reader()
-	ex := dwtest.Examiner{}
-	if err := ex.Populate(rdr); err != nil {
-		t.Fatalf("error reading DWARF: %v", err)
-	}
-
-	// Locate the main.ABC DIE
-	abcs := ex.Named("main.ABC")
-	if len(abcs) == 0 {
-		t.Fatalf("unable to locate DIE for main.ABC")
-	}
-	if len(abcs) != 1 {
-		t.Fatalf("more than one main.ABC DIE")
-	}
-	abcdie := abcs[0]
-
-	// Vet the DIE
-	if abcdie.Tag != dwarf.TagSubprogram {
-		t.Fatalf("unexpected tag %v on main.ABC DIE", abcdie.Tag)
-	}
+	abcdie := findSubprogramDIE(t, ex, "main.ABC")
 
 	// Call a helper to collect param info.
-	found := processParams(abcdie, &ex)
+	found := processParams(abcdie, ex)
 
 	// Make sure we see all of the expected params in the proper
 	// order, that they have the varparam attr, and the varparam is
@@ -1788,20 +1752,7 @@
 
 }
 `
-	dir := t.TempDir()
-	f := gobuild(t, dir, prog, DefaultOpt)
-	defer f.Close()
-
-	d, err := f.DWARF()
-	if err != nil {
-		t.Fatalf("error reading DWARF: %v", err)
-	}
-
-	rdr := d.Reader()
-	ex := dwtest.Examiner{}
-	if err := ex.Populate(rdr); err != nil {
-		t.Fatalf("error reading DWARF: %v", err)
-	}
+	_, ex := gobuildAndExamine(t, prog, DefaultOpt)
 
 	testcases := []struct {
 		tag      string
@@ -1828,22 +1779,10 @@
 	for _, tc := range testcases {
 		// Locate the proper DIE
 		which := fmt.Sprintf("main.%s", tc.tag)
-		tcs := ex.Named(which)
-		if len(tcs) == 0 {
-			t.Fatalf("unable to locate DIE for " + which)
-		}
-		if len(tcs) != 1 {
-			t.Fatalf("more than one " + which + " DIE")
-		}
-		die := tcs[0]
-
-		// Vet the DIE
-		if die.Tag != dwarf.TagSubprogram {
-			t.Fatalf("unexpected tag %v on "+which+" DIE", die.Tag)
-		}
+		die := findSubprogramDIE(t, ex, which)
 
 		// Examine params for this subprogram.
-		foundParams := processParams(die, &ex)
+		foundParams := processParams(die, ex)
 		if foundParams != tc.expected {
 			t.Errorf("check failed for testcase %s -- wanted:\n%s\ngot:%s\n",
 				tc.tag, tc.expected, foundParams)
@@ -1956,29 +1895,18 @@
 `
 
 	for _, opt := range []string{NoOpt, DefaultOpt} {
-		dir := t.TempDir()
-		f := gobuild(t, dir, prog, opt)
-		defer f.Close()
-		defer os.RemoveAll(dir)
+		opt := opt
+		t.Run(opt, func(t *testing.T) {
+			_, ex := gobuildAndExamine(t, prog, opt)
 
-		d, err := f.DWARF()
-		if err != nil {
-			t.Fatalf("error reading DWARF: %v", err)
-		}
-
-		rdr := d.Reader()
-		ex := dwtest.Examiner{}
-		if err := ex.Populate(rdr); err != nil {
-			t.Fatalf("error reading DWARF: %v", err)
-		}
-
-		// Locate the main.zeroSizedVariable DIE
-		abcs := ex.Named("zeroSizedVariable")
-		if len(abcs) == 0 {
-			t.Fatalf("unable to locate DIE for zeroSizedVariable")
-		}
-		if len(abcs) != 1 {
-			t.Fatalf("more than one zeroSizedVariable DIE")
-		}
+			// Locate the main.zeroSizedVariable DIE
+			abcs := ex.Named("zeroSizedVariable")
+			if len(abcs) == 0 {
+				t.Fatalf("unable to locate DIE for zeroSizedVariable")
+			}
+			if len(abcs) != 1 {
+				t.Fatalf("more than one zeroSizedVariable DIE")
+			}
+		})
 	}
 }