summaryrefslogtreecommitdiff
path: root/libgo/go/runtime/traceback_gccgo.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/runtime/traceback_gccgo.go')
-rw-r--r--libgo/go/runtime/traceback_gccgo.go122
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")