summaryrefslogtreecommitdiff
path: root/src/pkg/runtime/traceback.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/runtime/traceback.go')
-rw-r--r--src/pkg/runtime/traceback.go639
1 files changed, 0 insertions, 639 deletions
diff --git a/src/pkg/runtime/traceback.go b/src/pkg/runtime/traceback.go
deleted file mode 100644
index ec7be28dc..000000000
--- a/src/pkg/runtime/traceback.go
+++ /dev/null
@@ -1,639 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package runtime
-
-import "unsafe"
-
-// The code in this file implements stack trace walking for all architectures.
-// The most important fact about a given architecture is whether it uses a link register.
-// On systems with link registers, the prologue for a non-leaf function stores the
-// incoming value of LR at the bottom of the newly allocated stack frame.
-// On systems without link registers, the architecture pushes a return PC during
-// the call instruction, so the return PC ends up above the stack frame.
-// In this file, the return PC is always called LR, no matter how it was found.
-//
-// To date, the opposite of a link register architecture is an x86 architecture.
-// This code may need to change if some other kind of non-link-register
-// architecture comes along.
-//
-// The other important fact is the size of a pointer: on 32-bit systems the LR
-// takes up only 4 bytes on the stack, while on 64-bit systems it takes up 8 bytes.
-// Typically this is ptrSize.
-//
-// As an exception, amd64p32 has ptrSize == 4 but the CALL instruction still
-// stores an 8-byte return PC onto the stack. To accommodate this, we use regSize
-// as the size of the architecture-pushed return PC.
-//
-// usesLR is defined below. ptrSize and regSize are defined in stubs.go.
-
-const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386"
-
-var (
- deferprocPC = funcPC(deferproc)
- goexitPC = funcPC(goexit)
- jmpdeferPC = funcPC(jmpdefer)
- lessstackPC = funcPC(lessstack)
- mcallPC = funcPC(mcall)
- morestackPC = funcPC(morestack)
- mstartPC = funcPC(mstart)
- newprocPC = funcPC(newproc)
- newstackPC = funcPC(newstack)
- rt0_goPC = funcPC(rt0_go)
- sigpanicPC = funcPC(sigpanic)
-
- 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)
-
-// Generic traceback. Handles runtime stack prints (pcbuf == nil),
-// the runtime.Callers function (pcbuf != nil), as well as the garbage
-// collector (callback != nil). A little clunky to merge these, but avoids
-// duplicating the code and all its subtlety.
-func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max int, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer, printall bool) int {
- g := getg()
- gotraceback := gotraceback(nil)
- if pc0 == ^uintptr(0) && sp0 == ^uintptr(0) { // Signal to fetch saved values from gp.
- if gp.syscallstack != 0 {
- pc0 = gp.syscallpc
- sp0 = gp.syscallsp
- if usesLR {
- lr0 = 0
- }
- } else {
- pc0 = gp.sched.pc
- sp0 = gp.sched.sp
- if usesLR {
- lr0 = gp.sched.lr
- }
- }
- }
-
- nprint := 0
- var frame stkframe
- frame.pc = pc0
- frame.sp = sp0
- if usesLR {
- frame.lr = lr0
- }
- 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.
- if frame.pc == 0 {
- if usesLR {
- frame.pc = *(*uintptr)(unsafe.Pointer(frame.sp))
- frame.lr = 0
- } else {
- frame.pc = uintptr(*(*uintreg)(unsafe.Pointer(frame.sp)))
- frame.sp += regSize
- }
- }
-
- f := findfunc(frame.pc)
- if f == nil {
- if callback != nil {
- print("runtime: unknown pc ", hex(frame.pc), "\n")
- gothrow("unknown pc")
- }
- return 0
- }
- frame.fn = f
-
- n := 0
- stk := (*stktop)(unsafe.Pointer(gp.stackbase))
- for n < max {
- // Typically:
- // pc is the PC of the running function.
- // sp is the stack pointer at that program counter.
- // fp is the frame pointer (caller's stack pointer) at that program counter, or nil if unknown.
- // stk is the stack containing sp.
- // The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp.
- if frame.pc == lessstackPC {
- // Hit top of stack segment. Unwind to next segment.
- frame.pc = stk.gobuf.pc
- frame.sp = stk.gobuf.sp
- frame.lr = 0
- frame.fp = 0
- if printing && showframe(nil, gp) {
- print("----- stack segment boundary -----\n")
- }
- stk = (*stktop)(unsafe.Pointer(stk.stackbase))
- f = findfunc(frame.pc)
- if f == nil {
- print("runtime: unknown pc ", hex(frame.pc), " after stack split\n")
- if callback != nil {
- gothrow("unknown pc")
- }
- }
- frame.fn = f
- continue
- }
- f = frame.fn
-
- // Hook for handling Windows exception handlers. See traceback_windows.go.
- if systraceback != nil {
- changed, aborted := systraceback(f, (*stkframe)(noescape(unsafe.Pointer(&frame))), gp, printing, callback, v)
- if aborted {
- return n
- }
- if changed {
- continue
- }
- }
-
- // Found an actual function.
- // Derive frame pointer and link register.
- if frame.fp == 0 {
- frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc))
- if !usesLR {
- // On x86, call instruction pushes return PC before entering new function.
- frame.fp += regSize
- }
- }
- var flr *_func
- if topofstack(f) {
- frame.lr = 0
- flr = nil
- } else if usesLR && f.entry == jmpdeferPC {
- // jmpdefer modifies SP/LR/PC non-atomically.
- // If a profiling interrupt arrives during jmpdefer,
- // the stack unwind may see a mismatched register set
- // and get confused. Stop if we see PC within jmpdefer
- // to avoid that confusion.
- // See golang.org/issue/8153.
- if callback != nil {
- gothrow("traceback_arm: found jmpdefer when tracing with callback")
- }
- frame.lr = 0
- } else {
- if usesLR {
- if n == 0 && frame.sp < frame.fp || frame.lr == 0 {
- frame.lr = *(*uintptr)(unsafe.Pointer(frame.sp))
- }
- } else {
- if frame.lr == 0 {
- frame.lr = uintptr(*(*uintreg)(unsafe.Pointer(frame.fp - regSize)))
- }
- }
- flr = findfunc(frame.lr)
- if flr == nil {
- // This happens if you get a profiling interrupt at just the wrong time.
- // In that context it is okay to stop early.
- // But if callback is set, we're doing a garbage collection and must
- // get everything, so crash loudly.
- if callback != nil {
- print("runtime: unexpected return pc for ", gofuncname(f), " called from ", hex(frame.lr), "\n")
- gothrow("unknown caller pc")
- }
- }
- }
-
- frame.varp = frame.fp
- if !usesLR {
- // On x86, call instruction pushes return PC before entering new function.
- frame.varp -= regSize
- }
-
- // Derive size of arguments.
- // Most functions have a fixed-size argument block,
- // so we can use metadata about the function f.
- // Not all, though: there are some variadic functions
- // in package runtime and reflect, and for those we use call-specific
- // metadata recorded by f's caller.
- if callback != nil || printing {
- frame.argp = frame.fp
- if usesLR {
- frame.argp += ptrSize
- }
- if f.args != _ArgsSizeUnknown {
- frame.arglen = uintptr(f.args)
- } else if flr == nil {
- frame.arglen = 0
- } else if frame.lr == lessstackPC {
- frame.arglen = uintptr(stk.argsize)
- } else {
- i := funcarglen(flr, frame.lr)
- if i >= 0 {
- frame.arglen = uintptr(i)
- } else {
- var tmp string
- if flr != nil {
- tmp = gofuncname(flr)
- } else {
- tmp = "?"
- }
- print("runtime: unknown argument frame size for ", gofuncname(f), " called from ", hex(frame.lr), " [", tmp, "]\n")
- if callback != nil {
- gothrow("invalid stack")
- }
- frame.arglen = 0
- }
- }
- }
-
- // Determine function SP where deferproc would find its arguments.
- var sparg uintptr
- if usesLR {
- // On link register architectures, that's the standard bottom-of-stack plus 1 word
- // for the saved LR. If the previous frame was a direct call to newproc/deferproc,
- // however, the SP is three words lower than normal.
- // If the function has no frame at all - perhaps it just started, or perhaps
- // it is a leaf with no local variables - then we cannot possibly find its
- // SP in a defer, and we might confuse its SP for its caller's SP, so
- // leave sparg=0 in that case.
- if frame.fp != frame.sp {
- sparg = frame.sp + regSize
- if wasnewproc {
- sparg += 3 * regSize
- }
- }
- } else {
- // On x86 that's the standard bottom-of-stack, so SP exactly.
- // If the previous frame was a direct call to newproc/deferproc, however,
- // the SP is two words lower than normal.
- sparg = frame.sp
- if wasnewproc {
- sparg += 2 * ptrSize
- }
- }
-
- // Determine frame's 'continuation PC', where it can continue.
- // Normally this is the return address on the stack, but if sigpanic
- // is immediately below this function on the stack, then the frame
- // stopped executing due to a trap, and frame.pc is probably not
- // a safe point for looking up liveness information. In this panicking case,
- // the function either doesn't return at all (if it has no defers or if the
- // defers do not recover) or it returns from one of the calls to
- // deferproc a second time (if the corresponding deferred func recovers).
- // It suffices to assume that the most recent deferproc is the one that
- // 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 {
- 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
- }
- for _defer != nil && (_defer.argp == sparg || _defer.argp == _NoArgs) {
- _defer = _defer.link
- }
-
- if skip > 0 {
- skip--
- goto skipped
- }
-
- if pcbuf != nil {
- (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = frame.pc
- }
- if callback != nil {
- if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
- return n
- }
- }
- if printing {
- if printall || showframe(f, gp) {
- // Print during crash.
- // main(0x1, 0x2, 0x3)
- // /home/rsc/go/src/runtime/x.go:23 +0xf
- //
- tracepc := frame.pc // back up to CALL instruction for funcline.
- if n > 0 && frame.pc > f.entry && !waspanic {
- tracepc--
- }
- print(gofuncname(f), "(")
- argp := (*[100]uintptr)(unsafe.Pointer(frame.argp))
- for i := uintptr(0); i < frame.arglen/ptrSize; i++ {
- if i >= 10 {
- print(", ...")
- break
- }
- if i != 0 {
- print(", ")
- }
- print(hex(argp[i]))
- }
- print(")\n")
- var file string
- line := funcline(f, tracepc, &file)
- print("\t", file, ":", line)
- if frame.pc > f.entry {
- print(" +", hex(frame.pc-f.entry))
- }
- if g.m.throwing > 0 && gp == g.m.curg || gotraceback >= 2 {
- print(" fp=", hex(frame.fp), " sp=", hex(frame.sp))
- }
- print("\n")
- nprint++
- }
- }
- n++
-
- skipped:
- waspanic = f.entry == sigpanicPC
- wasnewproc = f.entry == newprocPC || f.entry == deferprocPC
-
- // Do not unwind past the bottom of the stack.
- if flr == nil {
- break
- }
-
- // Unwind to next frame.
- frame.fn = flr
- frame.pc = frame.lr
- frame.lr = 0
- frame.sp = frame.fp
- frame.fp = 0
-
- // On link register architectures, sighandler saves the LR on stack
- // before faking a call to sigpanic.
- if usesLR && waspanic {
- x := *(*uintptr)(unsafe.Pointer(frame.sp))
- frame.sp += ptrSize
- f = findfunc(frame.pc)
- frame.fn = f
- if f == nil {
- frame.pc = x
- } else if f.frame == 0 {
- frame.lr = x
- }
- }
- }
-
- if pcbuf == nil && callback == nil {
- 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:
- //
- // frame 1 defers d1
- // frame 2 defers d2
- // frame 3 defers d3
- // frame 4 panics
- // frame 4's panic starts running defers
- // frame 5, running d3, defers d4
- // frame 5 panics
- // frame 5's panic starts running defers
- // frame 6, running d4, garbage collects
- // frame 6, running d2, garbage collects
- //
- // During the execution of d4, the panic stack is d4 -> d3, which
- // is nested properly, and we'll treat frame 3 as resumable, because we
- // can find d3. (And in fact frame 3 is resumable. If d4 recovers
- // and frame 5 continues running, d3, d3 can recover and we'll
- // resume execution in (returning from) frame 3.)
- //
- // During the execution of d2, however, the panic stack is d2 -> d3,
- // which is inverted. The scan will match d2 to frame 2 but having
- // d2 on the stack until then means it will not match d3 to frame 3.
- // This is okay: if we're running d2, then all the defers after d2 have
- // completed and their corresponding frames are dead. Not finding d3
- // for frame 3 means we'll set frame 3's continpc == 0, which is correct
- // (frame 3 is dead). At the end of the walk the panic stack can thus
- // contain defers (d3 in this case) for dead frames. The inversion here
- // always indicates a dead frame, and the effect of the inversion on the
- // scan is to hide those dead frames, so the scan is still okay:
- // what's left on the panic stack are exactly (and only) the dead frames.
- //
- // We require callback != nil here because only when callback != nil
- // do we know that gentraceback is being called in a "must be correct"
- // context as opposed to a "best effort" context. The tracebacks with
- // callbacks only happen when everything is stopped nicely.
- // 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 {
- 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")
- }
-
- return n
-}
-
-func printcreatedby(gp *g) {
- // Show what created goroutine, except main goroutine (goid 1).
- pc := gp.gopc
- f := findfunc(pc)
- if f != nil && showframe(f, gp) && gp.goid != 1 {
- print("created by ", gofuncname(f), "\n")
- tracepc := pc // back up to CALL instruction for funcline.
- if pc > f.entry {
- tracepc -= _PCQuantum
- }
- var file string
- line := funcline(f, tracepc, &file)
- print("\t", file, ":", line)
- if pc > f.entry {
- print(" +", hex(pc-f.entry))
- }
- print("\n")
- }
-}
-
-func traceback(pc uintptr, sp uintptr, lr uintptr, gp *g) {
- var n int
- if readgstatus(gp)&^_Gscan == _Gsyscall {
- // Override signal registers if blocked in system call.
- pc = gp.syscallpc
- sp = gp.syscallsp
- }
- // Print traceback. By default, omits runtime frames.
- // If that means we print nothing at all, repeat forcing all frames printed.
- n = gentraceback(pc, sp, 0, gp, 0, nil, _TracebackMaxFrames, nil, nil, false)
- if n == 0 {
- n = gentraceback(pc, sp, 0, gp, 0, nil, _TracebackMaxFrames, nil, nil, true)
- }
- if n == _TracebackMaxFrames {
- print("...additional frames elided...\n")
- }
- printcreatedby(gp)
-}
-
-func callers(skip int, pcbuf *uintptr, m int) int {
- sp := getcallersp(unsafe.Pointer(&skip))
- pc := uintptr(getcallerpc(unsafe.Pointer(&skip)))
- return gentraceback(pc, sp, 0, getg(), skip, pcbuf, m, nil, nil, false)
-}
-
-func gcallers(gp *g, skip int, pcbuf *uintptr, m int) int {
- return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, pcbuf, m, nil, nil, false)
-}
-
-func showframe(f *_func, gp *g) bool {
- g := getg()
- if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig) {
- return true
- }
- traceback := gotraceback(nil)
- name := gostringnocopy(funcname(f))
-
- // Special case: always show runtime.panic frame, so that we can
- // see where a panic started in the middle of a stack trace.
- // See golang.org/issue/5832.
- if name == "runtime.panic" {
- return true
- }
-
- return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.")
-}
-
-func contains(s, t string) bool {
- if len(t) == 0 {
- return true
- }
- for i := 0; i < len(s); i++ {
- if s[i] == t[0] && hasprefix(s[i:], t) {
- return true
- }
- }
- return false
-}
-
-func hasprefix(s, t string) bool {
- return len(s) >= len(t) && s[:len(t)] == t
-}
-
-var gStatusStrings = [...]string{
- _Gidle: "idle",
- _Grunnable: "runnable",
- _Grunning: "running",
- _Gsyscall: "syscall",
- _Gwaiting: "waiting",
- _Gdead: "dead",
- _Genqueue: "enqueue",
- _Gcopystack: "copystack",
-}
-
-var gScanStatusStrings = [...]string{
- 0: "scan",
- _Grunnable: "scanrunnable",
- _Grunning: "scanrunning",
- _Gsyscall: "scansyscall",
- _Gwaiting: "scanwaiting",
- _Gdead: "scandead",
- _Genqueue: "scanenqueue",
-}
-
-func goroutineheader(gp *g) {
- gpstatus := readgstatus(gp)
-
- // Basic string status
- var status string
- if 0 <= gpstatus && gpstatus < uint32(len(gStatusStrings)) {
- status = gStatusStrings[gpstatus]
- } else if gpstatus&_Gscan != 0 && 0 <= gpstatus&^_Gscan && gpstatus&^_Gscan < uint32(len(gStatusStrings)) {
- status = gStatusStrings[gpstatus&^_Gscan]
- } else {
- status = "???"
- }
-
- // Override.
- if (gpstatus == _Gwaiting || gpstatus == _Gscanwaiting) && gp.waitreason != "" {
- status = gp.waitreason
- }
-
- // approx time the G is blocked, in minutes
- var waitfor int64
- gpstatus &^= _Gscan // drop the scan bit
- if (gpstatus == _Gwaiting || gpstatus == _Gsyscall) && gp.waitsince != 0 {
- waitfor = (nanotime() - gp.waitsince) / 60e9
- }
- print("goroutine ", gp.goid, " [", status)
- if waitfor >= 1 {
- print(", ", waitfor, " minutes")
- }
- if gp.lockedm != nil {
- print(", locked to thread")
- }
- print("]:\n")
-}
-
-func tracebackothers(me *g) {
- level := gotraceback(nil)
-
- // Show the current goroutine first, if we haven't already.
- g := getg()
- gp := g.m.curg
- if gp != nil && gp != me {
- print("\n")
- goroutineheader(gp)
- traceback(^uintptr(0), ^uintptr(0), 0, gp)
- }
-
- lock(&allglock)
- for _, gp := range allgs {
- if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || gp.issystem && level < 2 {
- continue
- }
- print("\n")
- goroutineheader(gp)
- if readgstatus(gp)&^_Gscan == _Grunning {
- print("\tgoroutine running on other thread; stack unavailable\n")
- printcreatedby(gp)
- } else {
- traceback(^uintptr(0), ^uintptr(0), 0, gp)
- }
- }
- unlock(&allglock)
-}
-
-// Does f mark the top of a goroutine stack?
-func topofstack(f *_func) bool {
- pc := f.entry
- return pc == goexitPC ||
- pc == mstartPC ||
- pc == mcallPC ||
- pc == morestackPC ||
- pc == lessstackPC ||
- pc == rt0_goPC ||
- externalthreadhandlerp != 0 && pc == externalthreadhandlerp
-}