| // 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" |
| ) |
| |
| 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, 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 |
| 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 |
| } |
| |
| //go:noescape |
| //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)+1, &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) |
| 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, 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, 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_") { |
| return true |
| } |
| |
| level, _, _ := gotraceback() |
| if level > 1 { |
| // Show all frames. |
| return true |
| } |
| |
| if name == "" { |
| return false |
| } |
| |
| // 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. |
| if name == "runtime.gopanic" && !firstFrame { |
| return true |
| } |
| |
| return 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. Here also check |
| // for mangled functions from runtime/<...>, which will be prefixed with |
| // "runtime..z2f". |
| func isExportedRuntime(name string) bool { |
| const n = len("runtime.") |
| if hasPrefix(name, "runtime..z2f") { |
| return true |
| } |
| 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", |
| _Gexitingsyscall: "exiting syscall", |
| } |
| |
| 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 != waitReasonZero { |
| status = gp.waitreason.String() |
| } |
| |
| // 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 != 0 { |
| print(", locked to thread") |
| } |
| print("]:\n") |
| } |
| |
| // 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) { |
| 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 = (uintptr)(noescape(unsafe.Pointer(&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, false) && 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 = (uintptr)(noescape(unsafe.Pointer(&tb))) |
| getTraceback(me, gp) |
| printtrace(tb.locbuf[:tb.c], nil) |
| printcreatedby(gp) |
| } |
| } |
| unlock(&allglock) |
| |
| me.context = context |
| me.stackcontext = stackcontext |
| } |