diff options
Diffstat (limited to 'libgo/go/runtime/traceback_gccgo.go')
-rw-r--r-- | libgo/go/runtime/traceback_gccgo.go | 122 |
1 files changed, 99 insertions, 23 deletions
diff --git a/libgo/go/runtime/traceback_gccgo.go b/libgo/go/runtime/traceback_gccgo.go index 72a83a5ab2d..7581798e852 100644 --- a/libgo/go/runtime/traceback_gccgo.go +++ b/libgo/go/runtime/traceback_gccgo.go @@ -21,16 +21,20 @@ func printcreatedby(gp *g) { tracepc -= sys.PCQuantum } function, file, line := funcfileline(tracepc, -1) - if function != "" && showframe(function, gp) && gp.goid != 1 { - print("created by ", function, "\n") - print("\t", file, ":", line) - if entry != 0 && pc > entry { - print(" +", hex(pc-entry)) - } - print("\n") + if function != "" && showframe(function, gp, false) && gp.goid != 1 { + printcreatedby1(function, file, line, entry, pc) } } +func printcreatedby1(function, file string, line int, entry, pc uintptr) { + print("created by ", function, "\n") + print("\t", file, ":", line) + if entry != 0 && pc > entry { + print(" +", hex(pc-entry)) + } + print("\n") +} + // tracebackg is used to collect stack traces from other goroutines. type tracebackg struct { gp *g @@ -63,50 +67,108 @@ func callers(skip int, locbuf []location) int { func traceback(skip int32) { var locbuf [100]location c := c_callers(skip+1, &locbuf[0], int32(len(locbuf)), false) - printtrace(locbuf[:c], getg()) - printcreatedby(getg()) + gp := getg() + printtrace(locbuf[:c], gp) + printcreatedby(gp) + + if gp.ancestors == nil { + return + } + for _, ancestor := range *gp.ancestors { + printAncestorTraceback(ancestor) + } +} + +// printAncestorTraceback prints the traceback of the given ancestor. +func printAncestorTraceback(ancestor ancestorInfo) { + print("[originating from goroutine ", ancestor.goid, "]:\n") + for fidx, pc := range ancestor.pcs { + function, file, line := funcfileline(pc, -1) + if showfuncinfo(function, fidx == 0) { + printAncestorTracebackFuncInfo(function, file, line, pc) + } + } + if len(ancestor.pcs) == _TracebackMaxFrames { + print("...additional frames elided...\n") + } + // Show what created goroutine, except main goroutine (goid 1). + function, file, line := funcfileline(ancestor.gopc, -1) + if function != "" && showfuncinfo(function, false) && ancestor.goid != 1 { + printcreatedby1(function, file, line, funcentry(ancestor.gopc), ancestor.gopc) + } +} + +// printAncestorTraceback prints the given function info at a given pc +// within an ancestor traceback. The precision of this info is reduced +// due to only have access to the pcs at the time of the caller +// goroutine being created. +func printAncestorTracebackFuncInfo(name, file string, line int, pc uintptr) { + if name == "runtime.gopanic" { + name = "panic" + } + print(name, "(...)\n") + print("\t", file, ":", line) + entry := funcentry(pc) + if pc > entry { + print(" +", hex(pc-entry)) + } + print("\n") } // printtrace prints a traceback from locbuf. func printtrace(locbuf []location, gp *g) { + nprint := 0 for i := range locbuf { - if showframe(locbuf[i].function, gp) { + if showframe(locbuf[i].function, gp, nprint == 0) { name := locbuf[i].function if name == "runtime.gopanic" { name = "panic" } print(name, "\n\t", locbuf[i].filename, ":", locbuf[i].lineno, "\n") + nprint++ } } } // showframe returns whether to print a frame in a traceback. // name is the function name. -func showframe(name string, gp *g) bool { +func showframe(name string, gp *g, firstFrame bool) bool { g := getg() if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) { return true } + return showfuncinfo(name, firstFrame) +} +func showfuncinfo(name string, firstFrame bool) bool { // Gccgo can trace back through C functions called via cgo. // We want to print those in the traceback. // But unless GOTRACEBACK > 1 (checked below), still skip // internal C functions and cgo-generated functions. - if name != "" && !contains(name, ".") && !hasprefix(name, "__go_") && !hasprefix(name, "_cgo_") { + if name != "" && !contains(name, ".") && !hasPrefix(name, "__go_") && !hasPrefix(name, "_cgo_") { return true } level, _, _ := gotraceback() + if level > 1 { + // Show all frames. + return true + } + + if name == "" { + return false + } - // Special case: always show runtime.gopanic frame, so that we can - // see where a panic started in the middle of a stack trace. + // Special case: always show runtime.gopanic frame + // in the middle of a stack trace, so that we can + // see the boundary between ordinary code and + // panic-induced deferred code. // See golang.org/issue/5832. - // __go_panic is the current gccgo name. - if name == "runtime.gopanic" || name == "__go_panic" { + if name == "runtime.gopanic" && !firstFrame { return true } - return level > 1 || contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name)) + return contains(name, ".") && (!hasPrefix(name, "runtime.") || isExportedRuntime(name)) } // isExportedRuntime reports whether name is an exported runtime function. @@ -115,7 +177,7 @@ func showframe(name string, gp *g) bool { // "runtime..z2f". func isExportedRuntime(name string) bool { const n = len("runtime.") - if hasprefix(name, "runtime..z2f") { + if hasPrefix(name, "runtime..z2f") { return true } return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z' @@ -169,10 +231,24 @@ func goroutineheader(gp *g) { print("]:\n") } -// isSystemGoroutine reports whether the goroutine g must be omitted in -// stack dumps and deadlock detector. -func isSystemGoroutine(gp *g) bool { - return gp.isSystemGoroutine +// isSystemGoroutine reports whether the goroutine g must be omitted +// in stack dumps and deadlock detector. This is any goroutine that +// starts at a runtime.* entry point, except for runtime.main and +// sometimes runtime.runfinq. +// +// If fixed is true, any goroutine that can vary between user and +// system (that is, the finalizer goroutine) is considered a user +// goroutine. +func isSystemGoroutine(gp *g, fixed bool) bool { + if !gp.isSystemGoroutine { + return false + } + if fixed && gp.isFinalizerGoroutine { + // This goroutine can vary. In fixed mode, + // always consider it a user goroutine. + return false + } + return true } func tracebackothers(me *g) { @@ -200,7 +276,7 @@ func tracebackothers(me *g) { lock(&allglock) for _, gp := range allgs { - if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || isSystemGoroutine(gp) && level < 2 { + if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || isSystemGoroutine(gp, false) && level < 2 { continue } print("\n") |