]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: print exported methods from the runtime in tracebacks
authorKeith Randall <khr@golang.org>
Mon, 3 Jul 2023 20:49:26 +0000 (13:49 -0700)
committerKeith Randall <khr@golang.org>
Tue, 30 Jan 2024 17:07:25 +0000 (17:07 +0000)
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 <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
src/runtime/crash_test.go
src/runtime/traceback.go

index 2ed0fd8f0718f84c862fd7f138fe066f3021cd69..7fc620ac50260c2fac2ba51759c210f0baedc960 100644 (file)
@@ -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)
index 4ca4ac51ad535fc9dde95004ae55f8fe26d6278e..1c75c447d25e470a813261bfef4724bae50b8b3a 100644 (file)
@@ -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