aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/traceback.go
diff options
context:
space:
mode:
authorKeith Randall <keithr@alum.mit.edu>2019-04-11 09:50:59 -0700
committerKeith Randall <khr@golang.org>2019-06-04 17:35:20 +0000
commitfff4f599fe1c21e411a99de5c9b3777d06ce0ce6 (patch)
tree8e048033606759d2e1ad5bb11085a5188ba3cdd0 /src/runtime/traceback.go
parent8343a0934df8f437938c55a6f0ff120c7c24a8bb (diff)
downloadgo-fff4f599fe1c21e411a99de5c9b3777d06ce0ce6.tar.gz
go-fff4f599fe1c21e411a99de5c9b3777d06ce0ce6.zip
cmd/compile,runtime: allocate defer records on the stack
When a defer is executed at most once in a function body, we can allocate the defer record for it on the stack instead of on the heap. This should make defers like this (which are very common) faster. This optimization applies to 363 out of the 370 static defer sites in the cmd/go binary. name old time/op new time/op delta Defer-4 52.2ns ± 5% 36.2ns ± 3% -30.70% (p=0.000 n=10+10) Fixes #6980 Update #14939 Change-Id: I697109dd7aeef9e97a9eeba2ef65ff53d3ee1004 Reviewed-on: https://go-review.googlesource.com/c/go/+/171758 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
Diffstat (limited to 'src/runtime/traceback.go')
-rw-r--r--src/runtime/traceback.go42
1 files changed, 7 insertions, 35 deletions
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index d817018501..ef48c9fa1f 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -148,11 +148,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
waspanic := false
cgoCtxt := gp.cgoCtxt
printing := pcbuf == nil && callback == nil
- _defer := gp._defer
-
- for _defer != nil && _defer.sp == _NoArgs {
- _defer = _defer.link
- }
// If the PC is zero, it's likely a nil function call.
// Start in the caller's frame.
@@ -319,15 +314,14 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// In the latter case, use a deferreturn call site as the continuation pc.
frame.continpc = frame.pc
if waspanic {
- // We match up defers with frames using the SP.
- // However, if the function has an empty stack
- // frame, then it's possible (on LR machines)
- // for multiple call frames to have the same
- // SP. But, since a function with no frame
- // can't push a defer, the defer can't belong
- // to that frame.
- if _defer != nil && _defer.sp == frame.sp && frame.sp != frame.fp {
+ if frame.fn.deferreturn != 0 {
frame.continpc = frame.fn.entry + uintptr(frame.fn.deferreturn) + 1
+ // Note: this may perhaps keep return variables alive longer than
+ // strictly necessary, as we are using "function has a defer statement"
+ // as a proxy for "function actually deferred something". It seems
+ // to be a minor drawback. (We used to actually look through the
+ // gp._defer for a defer corresponding to this function, but that
+ // is hard to do with defer records on the stack during a stack copy.)
// Note: the +1 is to offset the -1 that
// stack.go:getStackMap does to back up a return
// address make sure the pc is in the CALL instruction.
@@ -336,11 +330,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
}
}
- // Unwind our local defer stack past this frame.
- for _defer != nil && ((_defer.sp == frame.sp && frame.sp != frame.fp) || _defer.sp == _NoArgs) {
- _defer = _defer.link
- }
-
if callback != nil {
if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
return n
@@ -510,13 +499,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
n = nprint
}
- // If callback != nil, we're being called to gather stack information during
- // garbage collection or stack growth. In that context, require that we used
- // up the entire defer stack. If not, then there is a bug somewhere and the
- // garbage collection or stack growth may not have seen the correct picture
- // of the stack. Crash now instead of silently executing the garbage collection
- // or stack copy incorrectly and setting up for a mysterious crash later.
- //
// Note that panic != nil is okay here: there can be leftover panics,
// because the defers on the panic stack do not nest in frame order as
// they do on the defer stack. If you have:
@@ -557,16 +539,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// At other times, such as when gathering a stack for a profiling signal
// or when printing a traceback during a crash, everything may not be
// stopped nicely, and the stack walk may not be able to complete.
- // It's okay in those situations not to use up the entire defer stack:
- // incomplete information then is still better than nothing.
- if callback != nil && n < max && _defer != nil {
- print("runtime: g", gp.goid, ": leftover defer sp=", hex(_defer.sp), " pc=", hex(_defer.pc), "\n")
- for _defer = gp._defer; _defer != nil; _defer = _defer.link {
- print("\tdefer ", _defer, " sp=", hex(_defer.sp), " pc=", hex(_defer.pc), "\n")
- }
- throw("traceback has leftover defers")
- }
-
if callback != nil && n < max && frame.sp != gp.stktopsp {
print("runtime: g", gp.goid, ": frame.sp=", hex(frame.sp), " top=", hex(gp.stktopsp), "\n")
print("\tstack=[", hex(gp.stack.lo), "-", hex(gp.stack.hi), "] n=", n, " max=", max, "\n")