mirror of
https://github.com/golang/go
synced 2024-11-07 15:06:16 -07:00
a1e9148e3d
If traceback fails, it prints a helpful hex dump of the stack. But the hex numbers have no 0x prefix, which might make it a little unclear that they are hex. We only print two per line, so there is plenty of room for the 0x. Print it, which lets us delete a custom hex formatter. Also, in the translated <name+off> hints, print off in hex (with a 0x prefix). The offsets were previously decimal, which could have been confused for hex since none of the hex had 0x prefixes. And decimal is kind of useless anyway since the offsets shown in the main traceback are hex, so you can't easily match them up without mental base conversions. Just print hex everywhere, clearly marked by 0x. This CL is part of a stack adding windows/arm64 support (#36439), intended to land in the Go 1.17 cycle. This CL is, however, not windows/arm64-specific. It is cleanup meant to make the port (and future ports) easier. Change-Id: I72d26a4e41ada38b620bf8fe3576d787a2e59b47 Reviewed-on: https://go-review.googlesource.com/c/go/+/288809 Trust: Russ Cox <rsc@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
303 lines
6.0 KiB
Go
303 lines
6.0 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()
|
|
// Don't use the writebuf if gp.m is dying. We want anything
|
|
// written through gwrite to appear in the terminal rather
|
|
// than be written to in some buffer, if we're in a panicking state.
|
|
// Note that we can't just clear writebuf in the gp.m.dying case
|
|
// because a panic isn't allowed to have any write barriers.
|
|
if gp == nil || gp.writebuf == nil || gp.m.dying > 0 {
|
|
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))
|
|
}
|
|
|
|
var minhexdigits = 0 // protected by printlock
|
|
|
|
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 && len(buf)-i >= minhexdigits {
|
|
break
|
|
}
|
|
v /= 16
|
|
}
|
|
i--
|
|
buf[i] = 'x'
|
|
i--
|
|
buf[i] = '0'
|
|
gwrite(buf[i:])
|
|
}
|
|
|
|
func printpointer(p unsafe.Pointer) {
|
|
printhex(uint64(uintptr(p)))
|
|
}
|
|
func printuintptr(p uintptr) {
|
|
printhex(uint64(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) {
|
|
printlock()
|
|
var markbuf [1]byte
|
|
markbuf[0] = ' '
|
|
minhexdigits = int(unsafe.Sizeof(uintptr(0)) * 2)
|
|
for i := uintptr(0); p+i < end; i += sys.PtrSize {
|
|
if i%16 == 0 {
|
|
if i != 0 {
|
|
println()
|
|
}
|
|
print(hex(p+i), ": ")
|
|
}
|
|
|
|
if mark != nil {
|
|
markbuf[0] = mark(p + i)
|
|
if markbuf[0] == 0 {
|
|
markbuf[0] = ' '
|
|
}
|
|
}
|
|
gwrite(markbuf[:])
|
|
val := *(*uintptr)(unsafe.Pointer(p + i))
|
|
print(hex(val))
|
|
print(" ")
|
|
|
|
// Can we symbolize val?
|
|
fn := findfunc(val)
|
|
if fn.valid() {
|
|
print("<", funcname(fn), "+", hex(val-fn.entry), "> ")
|
|
}
|
|
}
|
|
minhexdigits = 0
|
|
println()
|
|
printunlock()
|
|
}
|