From: Keith Randall Date: Mon, 3 Jul 2023 20:49:26 +0000 (-0700) Subject: runtime: print exported methods from the runtime in tracebacks X-Git-Tag: go1.23rc1~1347 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=a0d477cb6d3173c860583ccf7aa7919687bddbca;p=gostls13.git runtime: print exported methods from the runtime in tracebacks We currently suppress runtime frames in tracebacks, except for exported functions. This CL also prints exported methods of exported types in tracebacks, for consistency. Change-Id: Ic65e7611621f0b210de5ae0c02b9d0a044f39fd6 Reviewed-on: https://go-review.googlesource.com/c/go/+/507736 Reviewed-by: Cherry Mui LUCI-TryBot-Result: Go LUCI Reviewed-by: Michael Knyszek Reviewed-by: Emmanuel Odeke --- diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 2ed0fd8f07..7fc620ac50 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -773,6 +773,16 @@ func init() { // We expect to crash, so exit 0 to indicate failure. os.Exit(0) } + if os.Getenv("GO_TEST_RUNTIME_NPE_READMEMSTATS") == "1" { + runtime.ReadMemStats(nil) + os.Exit(0) + } + if os.Getenv("GO_TEST_RUNTIME_NPE_FUNCMETHOD") == "1" { + var f *runtime.Func + _ = f.Entry() + os.Exit(0) + } + } func TestRuntimePanic(t *testing.T) { @@ -788,6 +798,32 @@ func TestRuntimePanic(t *testing.T) { } } +func TestTracebackRuntimeFunction(t *testing.T) { + testenv.MustHaveExec(t) + cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeFunction")) + cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_READMEMSTATS=1") + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + if err == nil { + t.Error("child process did not fail") + } else if want := "runtime.ReadMemStats"; !bytes.Contains(out, []byte(want)) { + t.Errorf("output did not contain expected string %q", want) + } +} + +func TestTracebackRuntimeMethod(t *testing.T) { + testenv.MustHaveExec(t) + cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeMethod")) + cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_FUNCMETHOD=1") + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + if err == nil { + t.Error("child process did not fail") + } else if want := "runtime.(*Func).Entry"; !bytes.Contains(out, []byte(want)) { + t.Errorf("output did not contain expected string %q", want) + } +} + // Test that g0 stack overflows are handled gracefully. func TestG0StackOverflow(t *testing.T) { testenv.MustHaveExec(t) diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 4ca4ac51ad..1c75c447d2 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -1133,10 +1133,32 @@ func showfuncinfo(sf srcFunc, firstFrame bool, calleeID abi.FuncID) bool { // isExportedRuntime reports whether name is an exported runtime function. // It is only for runtime functions, so ASCII A-Z is fine. -// TODO: this handles exported functions but not exported methods. func isExportedRuntime(name string) bool { - const n = len("runtime.") - return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z' + // Check and remove package qualifier. + n := len("runtime.") + if len(name) <= n || name[:n] != "runtime." { + return false + } + name = name[n:] + rcvr := "" + + // Extract receiver type, if any. + // For example, runtime.(*Func).Entry + i := len(name) - 1 + for i >= 0 && name[i] != '.' { + i-- + } + if i >= 0 { + rcvr = name[:i] + name = name[i+1:] + // Remove parentheses and star for pointer receivers. + if len(rcvr) >= 3 && rcvr[0] == '(' && rcvr[1] == '*' && rcvr[len(rcvr)-1] == ')' { + rcvr = rcvr[2 : len(rcvr)-1] + } + } + + // Exported functions and exported methods on exported types. + return len(name) > 0 && 'A' <= name[0] && name[0] <= 'Z' && (len(rcvr) == 0 || 'A' <= rcvr[0] && rcvr[0] <= 'Z') } // elideWrapperCalling reports whether a wrapper function that called