mirror of
https://github.com/golang/go
synced 2024-11-06 05:26:11 -07:00
dbd8f3d739
Currently, if anything goes wrong when printing a traceback, we simply cut off the traceback without any further diagnostics. Unfortunately, right now, we have a few issues that are difficult to debug because the traceback simply cuts off (#21431, #23484). This is an attempt to improve the debuggability of traceback failure by printing a diagnostic message plus a hex dump around the failed traceback frame when something goes wrong. The failures look like: goroutine 5 [running]: runtime: unexpected return pc for main.badLR2 called from 0xbad stack: frame={sp:0xc42004dfa8, fp:0xc42004dfc8} stack=[0xc42004d800,0xc42004e000) 000000c42004dea8: 0000000000000001 0000000000000001 000000c42004deb8: 000000c42004ded8 000000c42004ded8 000000c42004dec8: 0000000000427eea <runtime.dopanic+74> 000000c42004ded8 000000c42004ded8: 000000000044df70 <runtime.dopanic.func1+0> 000000c420001080 000000c42004dee8: 0000000000427b21 <runtime.gopanic+961> 000000c42004df08 000000c42004def8: 000000c42004df98 0000000000427b21 <runtime.gopanic+961> 000000c42004df08: 0000000000000000 0000000000000000 000000c42004df18: 0000000000000000 0000000000000000 000000c42004df28: 0000000000000000 0000000000000000 000000c42004df38: 0000000000000000 000000c420001080 000000c42004df48: 0000000000000000 0000000000000000 000000c42004df58: 0000000000000000 0000000000000000 000000c42004df68: 000000c4200010a0 0000000000000000 000000c42004df78: 00000000004c6400 00000000005031d0 000000c42004df88: 0000000000000000 0000000000000000 000000c42004df98: 000000c42004dfb8 00000000004ae7d9 <main.badLR2+73> 000000c42004dfa8: <00000000004c6400 00000000005031d0 000000c42004dfb8: 000000c42004dfd0 !0000000000000bad 000000c42004dfc8: >0000000000000000 0000000000000000 000000c42004dfd8: 0000000000451821 <runtime.goexit+1> 0000000000000000 000000c42004dfe8: 0000000000000000 0000000000000000 000000c42004dff8: 0000000000000000 main.badLR2(0x0) /go/src/runtime/testdata/testprog/badtraceback.go:42 +0x49 For #21431, #23484. Change-Id: I8718fc76ced81adb0b4b0b4f2293f3219ca80786 Reviewed-on: https://go-review.googlesource.com/89016 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
305 lines
5.6 KiB
Go
305 lines
5.6 KiB
Go
// Copyright 2009 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.
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"runtime/internal/atomic"
|
|
"runtime/internal/sys"
|
|
"unsafe"
|
|
)
|
|
|
|
// The compiler knows that a print of a value of this type
|
|
// should use printhex instead of printuint (decimal).
|
|
type hex uint64
|
|
|
|
func bytes(s string) (ret []byte) {
|
|
rp := (*slice)(unsafe.Pointer(&ret))
|
|
sp := stringStructOf(&s)
|
|
rp.array = sp.str
|
|
rp.len = sp.len
|
|
rp.cap = sp.len
|
|
return
|
|
}
|
|
|
|
var (
|
|
// printBacklog is a circular buffer of messages written with the builtin
|
|
// print* functions, for use in postmortem analysis of core dumps.
|
|
printBacklog [512]byte
|
|
printBacklogIndex int
|
|
)
|
|
|
|
// recordForPanic maintains a circular buffer of messages written by the
|
|
// runtime leading up to a process crash, allowing the messages to be
|
|
// extracted from a core dump.
|
|
//
|
|
// The text written during a process crash (following "panic" or "fatal
|
|
// error") is not saved, since the goroutine stacks will generally be readable
|
|
// from the runtime datastructures in the core file.
|
|
func recordForPanic(b []byte) {
|
|
printlock()
|
|
|
|
if atomic.Load(&panicking) == 0 {
|
|
// Not actively crashing: maintain circular buffer of print output.
|
|
for i := 0; i < len(b); {
|
|
n := copy(printBacklog[printBacklogIndex:], b[i:])
|
|
i += n
|
|
printBacklogIndex += n
|
|
printBacklogIndex %= len(printBacklog)
|
|
}
|
|
}
|
|
|
|
printunlock()
|
|
}
|
|
|
|
var debuglock mutex
|
|
|
|
// The compiler emits calls to printlock and printunlock around
|
|
// the multiple calls that implement a single Go print or println
|
|
// statement. Some of the print helpers (printslice, for example)
|
|
// call print recursively. There is also the problem of a crash
|
|
// happening during the print routines and needing to acquire
|
|
// the print lock to print information about the crash.
|
|
// For both these reasons, let a thread acquire the printlock 'recursively'.
|
|
|
|
func printlock() {
|
|
mp := getg().m
|
|
mp.locks++ // do not reschedule between printlock++ and lock(&debuglock).
|
|
mp.printlock++
|
|
if mp.printlock == 1 {
|
|
lock(&debuglock)
|
|
}
|
|
mp.locks-- // now we know debuglock is held and holding up mp.locks for us.
|
|
}
|
|
|
|
func printunlock() {
|
|
mp := getg().m
|
|
mp.printlock--
|
|
if mp.printlock == 0 {
|
|
unlock(&debuglock)
|
|
}
|
|
}
|
|
|
|
// write to goroutine-local buffer if diverting output,
|
|
// or else standard error.
|
|
func gwrite(b []byte) {
|
|
if len(b) == 0 {
|
|
return
|
|
}
|
|
recordForPanic(b)
|
|
gp := getg()
|
|
if gp == nil || gp.writebuf == nil {
|
|
writeErr(b)
|
|
return
|
|
}
|
|
|
|
n := copy(gp.writebuf[len(gp.writebuf):cap(gp.writebuf)], b)
|
|
gp.writebuf = gp.writebuf[:len(gp.writebuf)+n]
|
|
}
|
|
|
|
func printsp() {
|
|
printstring(" ")
|
|
}
|
|
|
|
func printnl() {
|
|
printstring("\n")
|
|
}
|
|
|
|
func printbool(v bool) {
|
|
if v {
|
|
printstring("true")
|
|
} else {
|
|
printstring("false")
|
|
}
|
|
}
|
|
|
|
func printfloat(v float64) {
|
|
switch {
|
|
case v != v:
|
|
printstring("NaN")
|
|
return
|
|
case v+v == v && v > 0:
|
|
printstring("+Inf")
|
|
return
|
|
case v+v == v && v < 0:
|
|
printstring("-Inf")
|
|
return
|
|
}
|
|
|
|
const n = 7 // digits printed
|
|
var buf [n + 7]byte
|
|
buf[0] = '+'
|
|
e := 0 // exp
|
|
if v == 0 {
|
|
if 1/v < 0 {
|
|
buf[0] = '-'
|
|
}
|
|
} else {
|
|
if v < 0 {
|
|
v = -v
|
|
buf[0] = '-'
|
|
}
|
|
|
|
// normalize
|
|
for v >= 10 {
|
|
e++
|
|
v /= 10
|
|
}
|
|
for v < 1 {
|
|
e--
|
|
v *= 10
|
|
}
|
|
|
|
// round
|
|
h := 5.0
|
|
for i := 0; i < n; i++ {
|
|
h /= 10
|
|
}
|
|
v += h
|
|
if v >= 10 {
|
|
e++
|
|
v /= 10
|
|
}
|
|
}
|
|
|
|
// format +d.dddd+edd
|
|
for i := 0; i < n; i++ {
|
|
s := int(v)
|
|
buf[i+2] = byte(s + '0')
|
|
v -= float64(s)
|
|
v *= 10
|
|
}
|
|
buf[1] = buf[2]
|
|
buf[2] = '.'
|
|
|
|
buf[n+2] = 'e'
|
|
buf[n+3] = '+'
|
|
if e < 0 {
|
|
e = -e
|
|
buf[n+3] = '-'
|
|
}
|
|
|
|
buf[n+4] = byte(e/100) + '0'
|
|
buf[n+5] = byte(e/10)%10 + '0'
|
|
buf[n+6] = byte(e%10) + '0'
|
|
gwrite(buf[:])
|
|
}
|
|
|
|
func printcomplex(c complex128) {
|
|
print("(", real(c), imag(c), "i)")
|
|
}
|
|
|
|
func printuint(v uint64) {
|
|
var buf [100]byte
|
|
i := len(buf)
|
|
for i--; i > 0; i-- {
|
|
buf[i] = byte(v%10 + '0')
|
|
if v < 10 {
|
|
break
|
|
}
|
|
v /= 10
|
|
}
|
|
gwrite(buf[i:])
|
|
}
|
|
|
|
func printint(v int64) {
|
|
if v < 0 {
|
|
printstring("-")
|
|
v = -v
|
|
}
|
|
printuint(uint64(v))
|
|
}
|
|
|
|
func printhex(v uint64) {
|
|
const dig = "0123456789abcdef"
|
|
var buf [100]byte
|
|
i := len(buf)
|
|
for i--; i > 0; i-- {
|
|
buf[i] = dig[v%16]
|
|
if v < 16 {
|
|
break
|
|
}
|
|
v /= 16
|
|
}
|
|
i--
|
|
buf[i] = 'x'
|
|
i--
|
|
buf[i] = '0'
|
|
gwrite(buf[i:])
|
|
}
|
|
|
|
func printpointer(p unsafe.Pointer) {
|
|
printhex(uint64(uintptr(p)))
|
|
}
|
|
|
|
func printstring(s string) {
|
|
gwrite(bytes(s))
|
|
}
|
|
|
|
func printslice(s []byte) {
|
|
sp := (*slice)(unsafe.Pointer(&s))
|
|
print("[", len(s), "/", cap(s), "]")
|
|
printpointer(sp.array)
|
|
}
|
|
|
|
func printeface(e eface) {
|
|
print("(", e._type, ",", e.data, ")")
|
|
}
|
|
|
|
func printiface(i iface) {
|
|
print("(", i.tab, ",", i.data, ")")
|
|
}
|
|
|
|
// hexdumpWords prints a word-oriented hex dump of [p, end).
|
|
//
|
|
// If mark != nil, it will be called with each printed word's address
|
|
// and should return a character mark to appear just before that
|
|
// word's value. It can return 0 to indicate no mark.
|
|
func hexdumpWords(p, end uintptr, mark func(uintptr) byte) {
|
|
p1 := func(x uintptr) {
|
|
var buf [2 * sys.PtrSize]byte
|
|
for i := len(buf) - 1; i >= 0; i-- {
|
|
if x&0xF < 10 {
|
|
buf[i] = byte(x&0xF) + '0'
|
|
} else {
|
|
buf[i] = byte(x&0xF) - 10 + 'a'
|
|
}
|
|
x >>= 4
|
|
}
|
|
gwrite(buf[:])
|
|
}
|
|
|
|
printlock()
|
|
var markbuf [1]byte
|
|
markbuf[0] = ' '
|
|
for i := uintptr(0); p+i < end; i += sys.PtrSize {
|
|
if i%16 == 0 {
|
|
if i != 0 {
|
|
println()
|
|
}
|
|
p1(p + i)
|
|
print(": ")
|
|
}
|
|
|
|
if mark != nil {
|
|
markbuf[0] = mark(p + i)
|
|
if markbuf[0] == 0 {
|
|
markbuf[0] = ' '
|
|
}
|
|
}
|
|
gwrite(markbuf[:])
|
|
val := *(*uintptr)(unsafe.Pointer(p + i))
|
|
p1(val)
|
|
print(" ")
|
|
|
|
// Can we symbolize val?
|
|
fn := findfunc(val)
|
|
if fn.valid() {
|
|
print("<", funcname(fn), "+", val-fn.entry, "> ")
|
|
}
|
|
}
|
|
println()
|
|
printunlock()
|
|
}
|