aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/panic.go
diff options
context:
space:
mode:
authorBryan C. Mills <bcmills@google.com>2019-10-16 20:41:53 +0000
committerBryan C. Mills <bcmills@google.com>2019-10-16 20:59:53 +0000
commitb76e6f88251712a21071d4ea11573bd8cdfa21de (patch)
tree8f61e6b95100c9ea5db743ed7934d0f253d3a58c /src/runtime/panic.go
parent2718789bc7937c58a7a65e53d9cc941b04682f99 (diff)
downloadgo-b76e6f88251712a21071d4ea11573bd8cdfa21de.tar.gz
go-b76e6f88251712a21071d4ea11573bd8cdfa21de.zip
Revert "cmd/compile, cmd/link, runtime: make defers low-cost through inline code and extra funcdata"
This reverts CL 190098. Reason for revert: broke several builders. Change-Id: I69161352f9ded02537d8815f259c4d391edd9220 Reviewed-on: https://go-review.googlesource.com/c/go/+/201519 Run-TryBot: Bryan C. Mills <bcmills@google.com> Reviewed-by: Austin Clements <austin@google.com> Reviewed-by: Dan Scales <danscales@google.com>
Diffstat (limited to 'src/runtime/panic.go')
-rw-r--r--src/runtime/panic.go316
1 files changed, 20 insertions, 296 deletions
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index 291a660b3e..5f33cd7c0c 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -10,19 +10,6 @@ import (
"unsafe"
)
-// We have two different ways of doing defers. The older way involves creating a
-// defer record at the time that a defer statement is executing and adding it to a
-// defer chain. This chain is inspected by the deferreturn call at all function
-// exits in order to run the appropriate defer calls. A cheaper way (which we call
-// open-coded defers) is used for functions in which no defer statements occur in
-// loops. In that case, we simply store the defer function/arg information into
-// specific stack slots at the point of each defer statement, as well as setting a
-// bit in a bitmask. At each function exit, we add inline code to directly make
-// the appropriate defer calls based on the bitmask and fn/arg information stored
-// on the stack. During panic/Goexit processing, the appropriate defer calls are
-// made using extra funcdata info that indicates the exact stack slots that
-// contain the bitmask and defer fn/args.
-
// Check to make sure we can really generate a panic. If the panic
// was generated from the runtime, or from inside malloc, then convert
// to a throw of msg.
@@ -276,24 +263,19 @@ func deferprocStack(d *_defer) {
// are initialized here.
d.started = false
d.heap = false
- d.openDefer = false
d.sp = getcallersp()
d.pc = getcallerpc()
- d.framepc = 0
- d.varp = 0
// The lines below implement:
// d.panic = nil
- // d.fp = nil
// d.link = gp._defer
// gp._defer = d
- // But without write barriers. The first three are writes to
+ // But without write barriers. The first two are writes to
// the stack so they don't need a write barrier, and furthermore
// are to uninitialized memory, so they must not use a write barrier.
- // The fourth write does not require a write barrier because we
+ // The third write does not require a write barrier because we
// explicitly mark all the defer structures, so we don't need to
// keep track of pointers to them with a write barrier.
*(*uintptr)(unsafe.Pointer(&d._panic)) = 0
- *(*uintptr)(unsafe.Pointer(&d.fd)) = 0
*(*uintptr)(unsafe.Pointer(&d.link)) = uintptr(unsafe.Pointer(gp._defer))
*(*uintptr)(unsafe.Pointer(&gp._defer)) = uintptr(unsafe.Pointer(d))
@@ -481,12 +463,8 @@ func freedefer(d *_defer) {
// started causing a nosplit stack overflow via typedmemmove.
d.siz = 0
d.started = false
- d.openDefer = false
d.sp = 0
d.pc = 0
- d.framepc = 0
- d.varp = 0
- d.fd = nil
// d._panic and d.fn must be nil already.
// If not, we would have called freedeferpanic or freedeferfn above,
// both of which throw.
@@ -515,11 +493,9 @@ func freedeferfn() {
// to have been called by the caller of deferreturn at the point
// just before deferreturn was called. The effect is that deferreturn
// is called again and again until there are no more deferred functions.
-//
-// Declared as nosplit, because the function should not be preempted once we start
-// modifying the caller's frame in order to reuse the frame to call the deferred
-// function.
-//
+// Cannot split the stack because we reuse the caller's frame to
+// call the deferred function.
+
// The single argument isn't actually used - it just has its address
// taken so it can be matched against pending defers.
//go:nosplit
@@ -533,15 +509,6 @@ func deferreturn(arg0 uintptr) {
if d.sp != sp {
return
}
- if d.openDefer {
- done := runOpenDeferFrame(gp, d)
- if !done {
- throw("unfinished open-coded defers in deferreturn")
- }
- gp._defer = d.link
- freedefer(d)
- return
- }
// Moving arguments around.
//
@@ -577,8 +544,6 @@ func Goexit() {
// This code is similar to gopanic, see that implementation
// for detailed comments.
gp := getg()
- addOneOpenDeferFrame(gp, getcallerpc(), unsafe.Pointer(getcallersp()))
-
for {
d := gp._defer
if d == nil {
@@ -589,26 +554,13 @@ func Goexit() {
d._panic.aborted = true
d._panic = nil
}
- if !d.openDefer {
- d.fn = nil
- gp._defer = d.link
- freedefer(d)
- continue
- }
+ d.fn = nil
+ gp._defer = d.link
+ freedefer(d)
+ continue
}
d.started = true
- if d.openDefer {
- done := runOpenDeferFrame(gp, d)
- if !done {
- // We should always run all defers in the frame,
- // since there is no panic associated with this
- // defer that can be recovered.
- throw("unfinished open-coded defers in Goexit")
- }
- addOneOpenDeferFrame(gp, 0, nil)
- } else {
- reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
- }
+ reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
if gp._defer != d {
throw("bad defer entry in Goexit")
}
@@ -655,182 +607,6 @@ func printpanics(p *_panic) {
print("\n")
}
-// addOneOpenDeferFrame scans the stack for the first frame (if any) with
-// open-coded defers and if it finds one, adds a single record to the defer chain
-// for that frame. If sp is non-nil, it starts the stack scan from the frame
-// specified by sp. If sp is nil, it uses the sp from the current defer record
-// (which has just been finished). Hence, it continues the stack scan from the
-// frame of the defer that just finished. It skips any frame that already has an
-// open-coded _defer record, which would have been been created from a previous
-// (unrecovered) panic.
-//
-// Note: All entries of the defer chain (including this new open-coded entry) have
-// their pointers (including sp) adjusted properly if the stack moves while
-// running deferred functions. Also, it is safe to pass in the sp arg (which is
-// the direct result of calling getcallersp()), because all pointer variables
-// (including arguments) are adjusted as needed during stack copies.
-func addOneOpenDeferFrame(gp *g, pc uintptr, sp unsafe.Pointer) {
- var prevDefer *_defer
- if sp == nil {
- prevDefer = gp._defer
- pc = prevDefer.framepc
- sp = unsafe.Pointer(prevDefer.sp)
- }
- systemstack(func() {
- gentraceback(pc, uintptr(sp), 0, gp, 0, nil, 0x7fffffff,
- func(frame *stkframe, unused unsafe.Pointer) bool {
- if prevDefer != nil && prevDefer.sp == frame.sp {
- // Skip the frame for the previous defer that
- // we just finished (and was used to set
- // where we restarted the stack scan)
- return true
- }
- f := frame.fn
- fd := funcdata(f, _FUNCDATA_OpenCodedDeferInfo)
- if fd == nil {
- return true
- }
- // Insert the open defer record in the
- // chain, in order sorted by sp.
- d := gp._defer
- var prev *_defer
- for d != nil {
- dsp := d.sp
- if frame.sp < dsp {
- break
- }
- if frame.sp == dsp {
- if !d.openDefer {
- throw("duplicated defer entry")
- }
- return true
- }
- prev = d
- d = d.link
- }
- if frame.fn.deferreturn == 0 {
- throw("missing deferreturn")
- }
-
- maxargsize, _ := readvarintUnsafe(fd)
- d1 := newdefer(int32(maxargsize))
- d1.openDefer = true
- d1._panic = nil
- // These are the pc/sp to set after we've
- // run a defer in this frame that did a
- // recover. We return to a special
- // deferreturn that runs any remaining
- // defers and then returns from the
- // function.
- d1.pc = frame.fn.entry + uintptr(frame.fn.deferreturn)
- d1.varp = frame.varp
- d1.fd = fd
- // Save the SP/PC associated with current frame,
- // so we can continue stack trace later if needed.
- d1.framepc = frame.pc
- d1.sp = frame.sp
- d1.link = d
- if prev == nil {
- gp._defer = d1
- } else {
- prev.link = d1
- }
- // Stop stack scanning after adding one open defer record
- return false
- },
- nil, 0)
- })
-}
-
-// readvarintUnsafe reads the uint32 in varint format starting at fd, and returns the
-// uint32 and a pointer to the byte following the varint.
-//
-// There is a similar function runtime.readvarint, which takes a slice of bytes,
-// rather than an unsafe pointer. These functions are duplicated, because one of
-// the two use cases for the functions would get slower if the functions were
-// combined.
-func readvarintUnsafe(fd unsafe.Pointer) (uint32, unsafe.Pointer) {
- var r uint32
- var shift int
- for {
- b := *(*uint8)((unsafe.Pointer(fd)))
- fd = add(fd, unsafe.Sizeof(b))
- if b < 128 {
- return r + uint32(b)<<shift, fd
- }
- r += ((uint32(b) &^ 128) << shift)
- shift += 7
- if shift > 28 {
- panic("Bad varint")
- }
- }
-}
-
-// runOpenDeferFrame runs the active open-coded defers in the frame specified by
-// d. It normally processes all active defers in the frame, but stops immediately
-// if a defer does a successful recover. It returns true if there are no
-// remaining defers to run in the frame.
-func runOpenDeferFrame(gp *g, d *_defer) bool {
- done := true
- fd := d.fd
-
- // Skip the maxargsize
- _, fd = readvarintUnsafe(fd)
- deferBitsOffset, fd := readvarintUnsafe(fd)
- nDefers, fd := readvarintUnsafe(fd)
- deferBits := *(*uint8)(unsafe.Pointer(d.varp - uintptr(deferBitsOffset)))
-
- for i := int(nDefers) - 1; i >= 0; i-- {
- // read the funcdata info for this defer
- var argWidth, closureOffset, hasRcvrOffset, rcvrOffset, nArgs uint32
- argWidth, fd = readvarintUnsafe(fd)
- closureOffset, fd = readvarintUnsafe(fd)
- hasRcvrOffset, fd = readvarintUnsafe(fd)
- if hasRcvrOffset > 0 {
- rcvrOffset, fd = readvarintUnsafe(fd)
- }
- nArgs, fd = readvarintUnsafe(fd)
- if deferBits&(1<<i) == 0 {
- for j := uint32(0); j < nArgs; j++ {
- _, fd = readvarintUnsafe(fd)
- _, fd = readvarintUnsafe(fd)
- _, fd = readvarintUnsafe(fd)
- }
- continue
- }
- closure := *(**funcval)(unsafe.Pointer(d.varp - uintptr(closureOffset)))
- d.fn = closure
- deferArgs := deferArgs(d)
- if hasRcvrOffset > 0 {
- *(*unsafe.Pointer)(deferArgs) = *(*unsafe.Pointer)((unsafe.Pointer)((d.varp - uintptr(rcvrOffset))))
- }
- for j := uint32(0); j < nArgs; j++ {
- var argOffset, argLen, argCallOffset uint32
- argOffset, fd = readvarintUnsafe(fd)
- argLen, fd = readvarintUnsafe(fd)
- argCallOffset, fd = readvarintUnsafe(fd)
- memmove(unsafe.Pointer(uintptr(deferArgs)+uintptr(argCallOffset)),
- unsafe.Pointer(d.varp-uintptr(argOffset)),
- uintptr(argLen))
- }
- deferBits = deferBits &^ (1 << i)
- *(*uint8)(unsafe.Pointer(d.varp - uintptr(deferBitsOffset))) = deferBits
- if d._panic != nil {
- d._panic.argp = unsafe.Pointer(getargp(0))
- }
- reflectcall(nil, unsafe.Pointer(closure), deferArgs, argWidth, argWidth)
- d.fn = nil
- // These args are just a copy, so can be cleared immediately
- memclrNoHeapPointers(deferArgs, uintptr(argWidth))
- if d._panic != nil && d._panic.recovered {
- done = deferBits == 0
- break
- }
- }
-
- return done
-}
-
// The implementation of the predeclared function panic.
func gopanic(e interface{}) {
gp := getg()
@@ -870,10 +646,6 @@ func gopanic(e interface{}) {
atomic.Xadd(&runningPanicDefers, 1)
- // By calculating getcallerpc/getcallersp here, we avoid scanning the
- // gopanic frame (stack scanning is slow...)
- addOneOpenDeferFrame(gp, getcallerpc(), unsafe.Pointer(getcallersp()))
-
for {
d := gp._defer
if d == nil {
@@ -887,16 +659,10 @@ func gopanic(e interface{}) {
d._panic.aborted = true
}
d._panic = nil
- if !d.openDefer {
- // For open-coded defers, we need to process the
- // defer again, in case there are any other defers
- // to call in the frame (not including the defer
- // call that caused the panic).
- d.fn = nil
- gp._defer = d.link
- freedefer(d)
- continue
- }
+ d.fn = nil
+ gp._defer = d.link
+ freedefer(d)
+ continue
}
// Mark defer as started, but keep on list, so that traceback
@@ -909,16 +675,8 @@ func gopanic(e interface{}) {
// will find d in the list and will mark d._panic (this panic) aborted.
d._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
- done := true
- if d.openDefer {
- done = runOpenDeferFrame(gp, d)
- if done && !d._panic.recovered {
- addOneOpenDeferFrame(gp, 0, nil)
- }
- } else {
- p.argp = unsafe.Pointer(getargp(0))
- reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
- }
+ p.argp = unsafe.Pointer(getargp(0))
+ reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
p.argp = nil
// reflectcall did not panic. Remove d.
@@ -926,52 +684,18 @@ func gopanic(e interface{}) {
throw("bad defer entry in panic")
}
d._panic = nil
+ d.fn = nil
+ gp._defer = d.link
// trigger shrinkage to test stack copy. See stack_test.go:TestStackPanic
//GC()
pc := d.pc
sp := unsafe.Pointer(d.sp) // must be pointer so it gets adjusted during stack copy
- if done {
- d.fn = nil
- gp._defer = d.link
- freedefer(d)
- }
+ freedefer(d)
if p.recovered {
atomic.Xadd(&runningPanicDefers, -1)
- if done {
- // Remove any remaining non-started, open-coded defer
- // entry after a recover (there's at most one, if we just
- // ran a non-open-coded defer), since the entry will
- // become out-dated and the defer will be executed
- // normally.
- d := gp._defer
- var prev *_defer
- for d != nil {
- if d.openDefer {
- if d.started {
- // This defer is started but we
- // are in the middle of a
- // defer-panic-recover inside of
- // it, so don't remove it or any
- // further defer entries
- break
- }
- if prev == nil {
- gp._defer = d.link
- } else {
- prev.link = d.link
- }
- freedefer(d)
- break
- } else {
- prev = d
- d = d.link
- }
- }
- }
-
gp._panic = p.link
// Aborted panics are marked but remain on the g.panic list.
// Remove them from the list.
@@ -1079,7 +803,7 @@ func recovery(gp *g) {
}
// Make the deferproc for this d return again,
- // this time returning 1. The calling function will
+ // this time returning 1. The calling function will
// jump to the standard return epilogue.
gp.sched.sp = sp
gp.sched.pc = pc