summaryrefslogtreecommitdiff
path: root/src/runtime/traceback.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-09-16 10:36:38 -0400
committerRuss Cox <rsc@golang.org>2014-09-16 10:36:38 -0400
commitc9afff3fcecc335ae25cef3ec5f4040fb30e2f9a (patch)
tree08460053cf605ffda28bdf07bda1879fd6160c17 /src/runtime/traceback.go
parent5819f77545053174374fc02ff3e09b6a6591c22c (diff)
downloadgo-c9afff3fcecc335ae25cef3ec5f4040fb30e2f9a.tar.gz
runtime: use traceback to traverse defer structures
This makes the GC and the stack copying agree about how to interpret the defer structures. Previously, only the stack copying treated them precisely. This removes an untyped memory allocation and fixes at least three copystack bugs. To make sure the GC can find the deferred argument frame until it has been copied, keep a Defer on the defer list during its execution. In addition to making it possible to remove the untyped memory allocation, keeping the Defer on the list fixes two races between copystack and execution of defers (in both gopanic and Goexit). The problem is that once the defer has been taken off the list, a stack copy that happens before the deferred arguments have been copied back to the stack will not update the arguments correctly. The new tests TestDeferPtrsPanic and TestDeferPtrsGoexit (variations on the existing TestDeferPtrs) pass now but failed before this CL. In addition to those fixes, keeping the Defer on the list helps correct a dangling pointer error during copystack. The traceback routines walk the Defer chain to provide information about where a panic may resume execution. When the executing Defer was not on the Defer chain but instead linked from the Panic chain, the traceback had to walk the Panic chain too. But Panic structs are on the stack and being updated by copystack. Traceback's use of the Panic chain while copystack is updating those structs means that it can follow an updated pointer and find itself reading from the new stack. The new stack is usually all zeros, so it sees an incorrect early end to the chain. The new TestPanicUseStack makes this happen at tip and dies when adjustdefers finds an unexpected argp. The new StackCopyPoison mode causes an earlier bad dereference instead. By keeping the Defer on the list, traceback can avoid walking the Panic chain at all, making it okay for copystack to update the Panics. We'd have the same problem for any Defers on the stack. There was only one: gopanic's dabort. Since we are not taking the executing Defer off the chain, we can use it to do what dabort was doing, and then there are no Defers on the stack ever, so it is okay for traceback to use the Defer chain even while copystack is executing: copystack cannot modify the Defer chain. LGTM=khr R=khr CC=dvyukov, golang-codereviews, iant, rlh https://codereview.appspot.com/141490043
Diffstat (limited to 'src/runtime/traceback.go')
-rw-r--r--src/runtime/traceback.go99
1 files changed, 56 insertions, 43 deletions
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 07b68d29b..9e95fa33d 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -45,8 +45,36 @@ var (
externalthreadhandlerp uintptr // initialized elsewhere
)
-// System-specific hook. See traceback_windows.go
-var systraceback func(*_func, *stkframe, *g, bool, func(*stkframe, unsafe.Pointer) bool, unsafe.Pointer) (changed, aborted bool)
+// Traceback over the deferred function calls.
+// Report them like calls that have been invoked but not started executing yet.
+func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer) {
+ var frame stkframe
+ for d := gp._defer; d != nil; d = d.link {
+ fn := d.fn
+ if fn == nil {
+ // Defer of nil function. Args don't matter.
+ frame.pc = 0
+ frame.fn = nil
+ frame.argp = 0
+ frame.arglen = 0
+ frame.argmap = nil
+ } else {
+ frame.pc = uintptr(fn.fn)
+ f := findfunc(frame.pc)
+ if f == nil {
+ print("runtime: unknown pc in defer ", hex(frame.pc), "\n")
+ gothrow("unknown pc")
+ }
+ frame.fn = f
+ frame.argp = uintptr(deferArgs(d))
+ setArgInfo(&frame, f, true)
+ }
+ frame.continpc = frame.pc
+ if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
+ return
+ }
+ }
+}
// Generic traceback. Handles runtime stack prints (pcbuf == nil),
// the runtime.Callers function (pcbuf != nil), as well as the garbage
@@ -81,15 +109,11 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
waspanic := false
wasnewproc := false
printing := pcbuf == nil && callback == nil
- panic := gp._panic
_defer := gp._defer
for _defer != nil && uintptr(_defer.argp) == _NoArgs {
_defer = _defer.link
}
- for panic != nil && panic._defer == nil {
- panic = panic.link
- }
// If the PC is zero, it's likely a nil function call.
// Start in the caller's frame.
@@ -187,25 +211,7 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
if usesLR {
frame.argp += ptrSize
}
- frame.arglen = uintptr(f.args)
- if callback != nil && f.args == _ArgsSizeUnknown {
- // Extract argument bitmaps for reflect stubs from the calls they made to reflect.
- switch gofuncname(f) {
- case "reflect.makeFuncStub", "reflect.methodValueCall":
- arg0 := frame.sp
- if usesLR {
- arg0 += ptrSize
- }
- fn := *(**[2]uintptr)(unsafe.Pointer(arg0))
- if fn[0] != f.entry {
- print("runtime: confused by ", gofuncname(f), "\n")
- gothrow("reflect mismatch")
- }
- bv := (*bitvector)(unsafe.Pointer(fn[1]))
- frame.arglen = uintptr(bv.n / 2 * ptrSize)
- frame.argmap = bv
- }
- }
+ setArgInfo(&frame, f, callback != nil)
}
// Determine function SP where deferproc would find its arguments.
@@ -246,19 +252,14 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
// returns; everything live at earlier deferprocs is still live at that one.
frame.continpc = frame.pc
if waspanic {
- if panic != nil && panic._defer.argp == sparg {
- frame.continpc = panic._defer.pc
- } else if _defer != nil && _defer.argp == sparg {
+ if _defer != nil && _defer.argp == sparg {
frame.continpc = _defer.pc
} else {
frame.continpc = 0
}
}
- // Unwind our local panic & defer stacks past this frame.
- for panic != nil && (panic._defer == nil || panic._defer.argp == sparg || panic._defer.argp == _NoArgs) {
- panic = panic.link
- }
+ // Unwind our local defer stack past this frame.
for _defer != nil && (_defer.argp == sparg || _defer.argp == _NoArgs) {
_defer = _defer.link
}
@@ -403,25 +404,37 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
if _defer != nil {
print("runtime: g", gp.goid, ": leftover defer argp=", hex(_defer.argp), " pc=", hex(_defer.pc), "\n")
}
- if panic != nil {
- print("runtime: g", gp.goid, ": leftover panic argp=", hex(panic._defer.argp), " pc=", hex(panic._defer.pc), "\n")
- }
for _defer = gp._defer; _defer != nil; _defer = _defer.link {
print("\tdefer ", _defer, " argp=", hex(_defer.argp), " pc=", hex(_defer.pc), "\n")
}
- for panic = gp._panic; panic != nil; panic = panic.link {
- print("\tpanic ", panic, " defer ", panic._defer)
- if panic._defer != nil {
- print(" argp=", hex(panic._defer.argp), " pc=", hex(panic._defer.pc))
- }
- print("\n")
- }
- gothrow("traceback has leftover defers or panics")
+ gothrow("traceback has leftover defers")
}
return n
}
+func setArgInfo(frame *stkframe, f *_func, needArgMap bool) {
+ frame.arglen = uintptr(f.args)
+ if needArgMap && f.args == _ArgsSizeUnknown {
+ // Extract argument bitmaps for reflect stubs from the calls they made to reflect.
+ switch gofuncname(f) {
+ case "reflect.makeFuncStub", "reflect.methodValueCall":
+ arg0 := frame.sp
+ if usesLR {
+ arg0 += ptrSize
+ }
+ fn := *(**[2]uintptr)(unsafe.Pointer(arg0))
+ if fn[0] != f.entry {
+ print("runtime: confused by ", gofuncname(f), "\n")
+ gothrow("reflect mismatch")
+ }
+ bv := (*bitvector)(unsafe.Pointer(fn[1]))
+ frame.arglen = uintptr(bv.n / 2 * ptrSize)
+ frame.argmap = bv
+ }
+ }
+}
+
func printcreatedby(gp *g) {
// Show what created goroutine, except main goroutine (goid 1).
pc := gp.gopc