diff options
Diffstat (limited to 'libgo/go/runtime/traceback_gccgo.go')
-rw-r--r-- | libgo/go/runtime/traceback_gccgo.go | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/libgo/go/runtime/traceback_gccgo.go b/libgo/go/runtime/traceback_gccgo.go new file mode 100644 index 0000000000..611aba91a4 --- /dev/null +++ b/libgo/go/runtime/traceback_gccgo.go @@ -0,0 +1,228 @@ +// Copyright 2016 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. + +// Traceback support for gccgo. +// The actual traceback code is written in C. + +package runtime + +import ( + "runtime/internal/sys" + _ "unsafe" // for go:linkname +) + +// For gccgo, use go:linkname to rename compiler-called functions to +// themselves, so that the compiler will export them. +// These are temporary for C runtime code to call. +//go:linkname traceback runtime.traceback +//go:linkname printtrace runtime.printtrace +//go:linkname goroutineheader runtime.goroutineheader +//go:linkname printcreatedby runtime.printcreatedby + +func printcreatedby(gp *g) { + // Show what created goroutine, except main goroutine (goid 1). + pc := gp.gopc + tracepc := pc // back up to CALL instruction for funcfileline. + entry := funcentry(tracepc) + if entry != 0 && tracepc > entry { + 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") + } +} + +// tracebackg is used to collect stack traces from other goroutines. +type tracebackg struct { + gp *g + locbuf [_TracebackMaxFrames]location + c int +} + +// location is a location in the program, used for backtraces. +type location struct { + pc uintptr + filename string + function string + lineno int +} + +//extern runtime_callers +func c_callers(skip int32, locbuf *location, max int32, keepThunks bool) int32 + +// callers returns a stack trace of the current goroutine. +// The gc version of callers takes []uintptr, but we take []location. +func callers(skip int, locbuf []location) int { + n := c_callers(int32(skip), &locbuf[0], int32(len(locbuf)), false) + return int(n) +} + +// traceback prints a traceback of the current goroutine. +// This differs from the gc version, which is given pc, sp, lr and g and +// can print a traceback of any goroutine. +func traceback(skip int32) { + var locbuf [100]location + c := c_callers(skip+1, &locbuf[0], int32(len(locbuf)), false) + printtrace(locbuf[:c], getg()) +} + +// printtrace prints a traceback from locbuf. +func printtrace(locbuf []location, gp *g) { + for i := range locbuf { + if showframe(locbuf[i].function, gp) { + print(locbuf[i].function, "\n\t", locbuf[i].filename, ":", locbuf[i].lineno, "\n") + } + } +} + +// showframe returns whether to print a frame in a traceback. +// name is the function name. +func showframe(name string, gp *g) bool { + g := getg() + if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) { + return true + } + + // 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 !contains(name, ".") && !hasprefix(name, "__go_") && !hasprefix(name, "_cgo_") { + return true + } + + level, _, _ := gotraceback() + + // Special case: always show runtime.gopanic frame, so that we can + // see where a panic started in the middle of a stack trace. + // See golang.org/issue/5832. + // __go_panic is the current gccgo name. + if name == "runtime.gopanic" || name == "__go_panic" { + return true + } + + return level > 1 || contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name)) +} + +// isExportedRuntime reports whether name is an exported runtime function. +// It is only for runtime functions, so ASCII A-Z is fine. +func isExportedRuntime(name string) bool { + const n = len("runtime.") + return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z' +} + +var gStatusStrings = [...]string{ + _Gidle: "idle", + _Grunnable: "runnable", + _Grunning: "running", + _Gsyscall: "syscall", + _Gwaiting: "waiting", + _Gdead: "dead", + _Gcopystack: "copystack", +} + +func goroutineheader(gp *g) { + gpstatus := readgstatus(gp) + + isScan := gpstatus&_Gscan != 0 + gpstatus &^= _Gscan // drop the scan bit + + // Basic string status + var status string + if 0 <= gpstatus && gpstatus < uint32(len(gStatusStrings)) { + status = gStatusStrings[gpstatus] + } else { + status = "???" + } + + // Override. + if gpstatus == _Gwaiting && gp.waitreason != "" { + status = gp.waitreason + } + + // approx time the G is blocked, in minutes + var waitfor int64 + if (gpstatus == _Gwaiting || gpstatus == _Gsyscall) && gp.waitsince != 0 { + waitfor = (nanotime() - gp.waitsince) / 60e9 + } + print("goroutine ", gp.goid, " [", status) + if isScan { + print(" (scan)") + } + if waitfor >= 1 { + print(", ", waitfor, " minutes") + } + if gp.lockedm != nil { + print(", locked to thread") + } + print("]:\n") +} + +// isSystemGoroutine reports whether the goroutine g must be omitted in +// stack dumps and deadlock detector. +func isSystemGoroutine(gp *g) bool { + // FIXME. + return false +} + +func tracebackothers(me *g) { + var tb tracebackg + tb.gp = me + + level, _, _ := gotraceback() + + // 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) + gp.traceback = &tb + getTraceback(me, gp) + printtrace(tb.locbuf[:tb.c], nil) + printcreatedby(gp) + } + + lock(&allglock) + for _, gp := range allgs { + if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || isSystemGoroutine(gp) && level < 2 { + continue + } + print("\n") + goroutineheader(gp) + + // gccgo's only mechanism for doing a stack trace is + // _Unwind_Backtrace. And that only works for the + // current thread, not for other random goroutines. + // So we need to switch context to the goroutine, get + // the backtrace, and then switch back. + // + // This means that if g is running or in a syscall, we + // can't reliably print a stack trace. FIXME. + + // Note: gp.m == g.m occurs when tracebackothers is + // called from a signal handler initiated during a + // systemstack call. The original G is still in the + // running state, and we want to print its stack. + if gp.m != g.m && readgstatus(gp)&^_Gscan == _Grunning { + print("\tgoroutine running on other thread; stack unavailable\n") + printcreatedby(gp) + } else if readgstatus(gp)&^_Gscan == _Gsyscall { + print("\tgoroutine in C code; stack unavailable\n") + printcreatedby(gp) + } else { + gp.traceback = &tb + getTraceback(me, gp) + printtrace(tb.locbuf[:tb.c], nil) + printcreatedby(gp) + } + } + unlock(&allglock) +} |