From 184474b3e823dae7dd5d6a80d28c0405dbaf5ec1 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Mon, 11 Sep 2023 13:05:16 -0400 Subject: [PATCH] dwtest: add explicit testpoint for runtime.throw Add a new testpoint that verifies the DWARF var location for the incoming parameter of the function 'runtime.throw'. We already have testpoings that validate DWARF for regular user-written functions that take a single string argument (as does 'runtime.throw'), but it is good to test the function in the runtime as well, since the rules for compiling the runtime are special, and we occasionally problems with DWARF generation can crop up that are runtime-specific. Fixes golang/go#62523. Cq-Include-Trybots: luci.golang.try:x_debug-gotip-linux-amd64-longtest Change-Id: I6558d9f12b22c997535cfc23d4b0758b10bd9548 Reviewed-on: https://go-review.googlesource.com/c/debug/+/526836 Reviewed-by: Alessandro Arzilli Reviewed-by: Hyang-Ah Hana Kim LUCI-TryBot-Result: Go LUCI --- dwtest/dwloc_test.go | 47 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/dwtest/dwloc_test.go b/dwtest/dwloc_test.go index 17aceb3..a0b7dca 100644 --- a/dwtest/dwloc_test.go +++ b/dwtest/dwloc_test.go @@ -76,20 +76,29 @@ func copyFilesForHarness(t *testing.T, dir string) string { return bd } -// buildHarness builds the helper program "dwdumploc.exe". -func buildHarness(t *testing.T, dir string) string { +// buildHarness builds the helper program "dwdumploc.exe" +// and a companion executable "dwdumploc.noopt.exe", built +// with "-gcflags=all=-l -N". +func buildHarness(t *testing.T, dir string) (string, string) { // Copy source files into build dir. bd := copyFilesForHarness(t, dir) - // Run build. + // Run builds. harnessPath := filepath.Join(dir, "dumpdwloc.exe") cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", harnessPath) cmd.Dir = bd if b, err := cmd.CombinedOutput(); err != nil { t.Fatalf("build failed (%v): %s", err, b) } - return harnessPath + + nooptHarnessPath := filepath.Join(dir, "dumpdwloc.exe") + cmd = exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-l -N", "-o", nooptHarnessPath) + cmd.Dir = bd + if b, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("build failed (%v): %s", err, b) + } + return harnessPath, nooptHarnessPath } // runHarness runs our previously built harness exec on a Go binary @@ -107,7 +116,7 @@ func runHarness(t *testing.T, harnessPath string, exePath string, fcn string) st return strings.TrimSpace(string(b.Bytes())) } -// gobuild is a helper to bulid a Go program from source code, +// gobuild is a helper to build a Go program from source code, // so that we can inspect selected bits of DWARF in the resulting binary. // Return value is binary path. func gobuild(t *testing.T, sourceCode string, pname string, dir string) string { @@ -227,6 +236,28 @@ func testIssue46845(t *testing.T, harnessPath string, ppath string) { } } +// testRuntimeThrow verifies that we have well-formed DWARF for the +// single input parameter of 'runtime.throw'. This function is +// particularly important to handle correctly, since it is +// special-cased by Delve. The code below checks that things are ok +// both for the regular optimized case and the "-gcflags=all=-l -N" +// case, which Delve users are often selecting. +func testRuntimeThrow(t *testing.T, harnessPath, nooptHarnessPath, ppath string) { + expected := map[string]string{ + "amd64": "1: in-param \"s\" loc=\"{ [0: S=8 RAX] [1: S=8 RBX] }\"", + "arm64": "1: in-param \"s\" loc=\"{ [0: S=8 R0] [1: S=8 R1] }\"", + } + fname := "runtime.throw" + harnesses := []string{harnessPath, nooptHarnessPath} + for _, harness := range harnesses { + got := runHarness(t, harness, ppath, fname) + want := expected[runtime.GOARCH] + if got != want { + t.Errorf("failed RuntimeThrow arch %s, harness %s:\ngot: %q\nwant: %q", runtime.GOARCH, harness, got, want) + } + } +} + func TestDwarfVariableLocations(t *testing.T) { testenv.NeedsGo1Point(t, 18) testenv.MustHaveGoBuild(t) @@ -257,7 +288,7 @@ func TestDwarfVariableLocations(t *testing.T) { } // Build test harness. - harnessPath := buildHarness(t, tdir) + harnessPath, nooptHarnessPath := buildHarness(t, tdir) // Build program to inspect. NB: we're building at default (with // optimization); it might also be worth doing a "-l -N" build @@ -273,4 +304,8 @@ func TestDwarfVariableLocations(t *testing.T) { t.Parallel() testIssue46845(t, harnessPath, ppath) }) + t.Run("RuntimeThrow", func(t *testing.T) { + t.Parallel() + testRuntimeThrow(t, harnessPath, nooptHarnessPath, ppath) + }) }