| // 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 name != "" && !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 { |
| return gp.isSystemGoroutine |
| } |
| |
| func tracebackothers(me *g) { |
| var tb tracebackg |
| tb.gp = me |
| |
| // The getTraceback function will modify me's stack context. |
| // Preserve it in case we have been called via systemstack. |
| context := me.context |
| stackcontext := me.stackcontext |
| |
| 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) |
| |
| me.context = context |
| me.stackcontext = stackcontext |
| } |