aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/panic.go
diff options
context:
space:
mode:
authorMichael Pratt <mpratt@google.com>2022-03-04 13:24:04 -0500
committerMichael Pratt <mpratt@google.com>2022-04-28 16:50:31 +0000
commit29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4 (patch)
tree74ea0c8d0bed2673984f9c0767c40ace72fc0bb2 /src/runtime/panic.go
parent4bb45f7549548add8222aa4f3040d0e2120691a9 (diff)
downloadgo-29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4.tar.xz
runtime: differentiate "user" and "system" throws
"User" throws are throws due to some invariant broken by the application. "System" throws are due to some invariant broken by the runtime, environment, etc (i.e., not the fault of the application). This CL sends "user" throws through the new fatal. Currently this function is identical to throw, but with a different name to clearly differentiate the throw type in the stack trace, and hopefully be a bit more clear to users what it means. This CL changes a few categories of throw to fatal: 1. Concurrent map read/write. 2. Deadlock detection. 3. Unlock of unlocked sync.Mutex. 4. Inconsistent results from syscall.AllThreadsSyscall. "Thread exhaustion" and "out of memory" (usually address space full) throws are additional throws that are arguably the fault of user code, but I've left off for now because there is no specific invariant that they have broken to get into these states. For #51485 Change-Id: I713276a6c290fd34a6563e6e9ef378669d74ae32 Reviewed-on: https://go-review.googlesource.com/c/go/+/390420 TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com> Run-TryBot: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/runtime/panic.go')
-rw-r--r--src/runtime/panic.go39
1 files changed, 32 insertions, 7 deletions
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index e4cc7bfb31..2e6f7af2ce 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -986,6 +986,15 @@ func sync_throw(s string) {
throw(s)
}
+//go:linkname sync_fatal sync.fatal
+func sync_fatal(s string) {
+ fatal(s)
+}
+
+// throw triggers a fatal error that dumps a stack trace and exits.
+//
+// throw should be used for runtime-internal fatal errors where Go itself,
+// rather than user code, may be at fault for the failure.
//go:nosplit
func throw(s string) {
// Everything throw does should be recursively nosplit so it
@@ -993,12 +1002,23 @@ func throw(s string) {
systemstack(func() {
print("fatal error: ", s, "\n")
})
- gp := getg()
- if gp.m.throwing == 0 {
- gp.m.throwing = 1
- }
+
+ fatalthrow()
+}
+
+// fatal triggers a fatal error that dumps a stack trace and exits.
+//
+// fatal is equivalent to throw, but is used when user code is expected to be
+// at fault for the failure, such as racing map writes.
+//go:nosplit
+func fatal(s string) {
+ // Everything fatal does should be recursively nosplit so it
+ // can be called even when it's unsafe to grow the stack.
+ systemstack(func() {
+ print("fatal error: ", s, "\n")
+ })
+
fatalthrow()
- *(*int)(nil) = 0 // not reached
}
// runningPanicDefers is non-zero while running deferred functions for panic.
@@ -1047,8 +1067,13 @@ func fatalthrow() {
pc := getcallerpc()
sp := getcallersp()
gp := getg()
- // Switch to the system stack to avoid any stack growth, which
- // may make things worse if the runtime is in a bad state.
+
+ if gp.m.throwing == 0 {
+ gp.m.throwing = 1
+ }
+
+ // Switch to the system stack to avoid any stack growth, which may make
+ // things worse if the runtime is in a bad state.
systemstack(func() {
startpanic_m()