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 <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>
This commit is contained in:
Keith Randall 2023-07-03 13:49:26 -07:00
parent 65f056d07a
commit a0d477cb6d
2 changed files with 61 additions and 3 deletions

View file

@ -773,6 +773,16 @@ func init() {
// We expect to crash, so exit 0 to indicate failure. // We expect to crash, so exit 0 to indicate failure.
os.Exit(0) 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) { 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. // Test that g0 stack overflows are handled gracefully.
func TestG0StackOverflow(t *testing.T) { func TestG0StackOverflow(t *testing.T) {
testenv.MustHaveExec(t) testenv.MustHaveExec(t)

View file

@ -1133,10 +1133,32 @@ func showfuncinfo(sf srcFunc, firstFrame bool, calleeID abi.FuncID) bool {
// isExportedRuntime reports whether name is an exported runtime function. // isExportedRuntime reports whether name is an exported runtime function.
// It is only for runtime functions, so ASCII A-Z is fine. // 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 { func isExportedRuntime(name string) bool {
const n = len("runtime.") // Check and remove package qualifier.
return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z' 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 // elideWrapperCalling reports whether a wrapper function that called