// 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) }