mirror of
https://github.com/golang/go
synced 2024-11-06 10:36:13 -07:00
e0aedfb496
When a Go program crashes with GOTRACEBACK=crash, the OS creates a core dump. Include the text-formatted output of some of the cause of that crash in the core dump. Output printed by the runtime before crashing is maintained in a circular buffer to allow access to messages that may be printed immediately before calling runtime.throw. The stack traces printed by the runtime as it crashes are not stored. The information required to recreate them should be included in the core file. Updates #16893 There are no tests covering the generation of core dumps; this change has not added any. This adds (reentrant) locking to runtime.gwrite, which may have an undesired performance impact. Change-Id: Ia2463be3c12429354d290bdec5f3c8d565d1a2c3 Reviewed-on: https://go-review.googlesource.com/32013 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
252 lines
4.5 KiB
Go
252 lines
4.5 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"
|
|
"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 (printsp, 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() {
|
|
print(" ")
|
|
}
|
|
|
|
func printnl() {
|
|
print("\n")
|
|
}
|
|
|
|
func printbool(v bool) {
|
|
if v {
|
|
print("true")
|
|
} else {
|
|
print("false")
|
|
}
|
|
}
|
|
|
|
func printfloat(v float64) {
|
|
switch {
|
|
case v != v:
|
|
print("NaN")
|
|
return
|
|
case v+v == v && v > 0:
|
|
print("+Inf")
|
|
return
|
|
case v+v == v && v < 0:
|
|
print("-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 {
|
|
print("-")
|
|
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, ")")
|
|
}
|