mirror of
https://github.com/golang/go
synced 2024-11-19 07:14:45 -07:00
runtime: support symbolic backtrace of C code in a cgo crash
The new function runtime.SetCgoTraceback may be used to register stack traceback and symbolizer functions, written in C, to do a stack traceback from cgo code. There is a sample implementation of runtime.SetCgoSymbolizer at github.com/ianlancetaylor/cgosymbolizer. Just importing that package is sufficient to get symbolic C backtraces. Currently only supported on linux/amd64. Change-Id: If96ee2eb41c6c7379d407b9561b87557bfe47341 Reviewed-on: https://go-review.googlesource.com/17761 Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
b64f549ba9
commit
ea306ae625
@ -16,6 +16,7 @@ import "unsafe"
|
|||||||
//go:linkname _cgo_thread_start _cgo_thread_start
|
//go:linkname _cgo_thread_start _cgo_thread_start
|
||||||
//go:linkname _cgo_sys_thread_create _cgo_sys_thread_create
|
//go:linkname _cgo_sys_thread_create _cgo_sys_thread_create
|
||||||
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
|
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
|
||||||
|
//go:linkname _cgo_callers _cgo_callers
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_cgo_init unsafe.Pointer
|
_cgo_init unsafe.Pointer
|
||||||
@ -24,6 +25,7 @@ var (
|
|||||||
_cgo_thread_start unsafe.Pointer
|
_cgo_thread_start unsafe.Pointer
|
||||||
_cgo_sys_thread_create unsafe.Pointer
|
_cgo_sys_thread_create unsafe.Pointer
|
||||||
_cgo_notify_runtime_init_done unsafe.Pointer
|
_cgo_notify_runtime_init_done unsafe.Pointer
|
||||||
|
_cgo_callers unsafe.Pointer
|
||||||
)
|
)
|
||||||
|
|
||||||
// iscgo is set to true by the runtime/cgo package
|
// iscgo is set to true by the runtime/cgo package
|
||||||
|
@ -92,5 +92,13 @@ var _cgo_sys_thread_create = &x_cgo_sys_thread_create
|
|||||||
var x_cgo_notify_runtime_init_done byte
|
var x_cgo_notify_runtime_init_done byte
|
||||||
var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done
|
var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done
|
||||||
|
|
||||||
|
// Calls the traceback function passed to SetCgoTraceback.
|
||||||
|
|
||||||
|
//go:cgo_import_static x_cgo_callers
|
||||||
|
//go:linkname x_cgo_callers x_cgo_callers
|
||||||
|
//go:linkname _cgo_callers _cgo_callers
|
||||||
|
var x_cgo_callers byte
|
||||||
|
var _cgo_callers = &x_cgo_callers
|
||||||
|
|
||||||
//go:cgo_export_static _cgo_topofstack
|
//go:cgo_export_static _cgo_topofstack
|
||||||
//go:cgo_export_dynamic _cgo_topofstack
|
//go:cgo_export_dynamic _cgo_topofstack
|
||||||
|
29
src/runtime/cgo/gcc_traceback.c
Normal file
29
src/runtime/cgo/gcc_traceback.c
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build cgo
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct cgoTracebackArg {
|
||||||
|
uintptr_t Context;
|
||||||
|
uintptr_t* Buf;
|
||||||
|
uintptr_t Max;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the user's traceback function and then call sigtramp.
|
||||||
|
// The runtime signal handler will jump to this code.
|
||||||
|
// We do it this way so that the user's traceback function will be called
|
||||||
|
// by a C function with proper unwind info.
|
||||||
|
void
|
||||||
|
x_cgo_callers(uintptr_t sig, void *info, void *context, void (*cgoTraceback)(struct cgoTracebackArg*), uintptr_t* cgoCallers, void (*sigtramp)(uintptr_t, void*, void*)) {
|
||||||
|
struct cgoTracebackArg arg;
|
||||||
|
|
||||||
|
arg.Context = 0;
|
||||||
|
arg.Buf = cgoCallers;
|
||||||
|
arg.Max = 32; // must match len(runtime.cgoCallers)
|
||||||
|
(*cgoTraceback)(&arg);
|
||||||
|
sigtramp(sig, info, context);
|
||||||
|
}
|
@ -84,6 +84,10 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Addresses collected in a cgo backtrace when crashing.
|
||||||
|
// Length must match arg.Max in x_cgo_callers in runtime/cgo/gcc_traceback.c.
|
||||||
|
type cgoCallers [32]uintptr
|
||||||
|
|
||||||
// Call from Go to C.
|
// Call from Go to C.
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
func cgocall(fn, arg unsafe.Pointer) int32 {
|
func cgocall(fn, arg unsafe.Pointer) int32 {
|
||||||
@ -109,6 +113,14 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
|
|||||||
mp.ncgo++
|
mp.ncgo++
|
||||||
defer endcgo(mp)
|
defer endcgo(mp)
|
||||||
|
|
||||||
|
// Allocate memory to hold a cgo traceback if the cgo call crashes.
|
||||||
|
if mp.cgoCallers == nil {
|
||||||
|
mp.cgoCallers = new(cgoCallers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset traceback.
|
||||||
|
mp.cgoCallers[0] = 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Announce we are entering a system call
|
* Announce we are entering a system call
|
||||||
* so that the scheduler knows to create another
|
* so that the scheduler knows to create another
|
||||||
|
@ -209,3 +209,15 @@ func TestCgoCCodeSIGPROF(t *testing.T) {
|
|||||||
t.Errorf("expected %q got %v", want, got)
|
t.Errorf("expected %q got %v", want, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCgoCrashTraceback(t *testing.T) {
|
||||||
|
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
||||||
|
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
got := runTestProg(t, "testprogcgo", "CrashTraceback")
|
||||||
|
for i := 1; i <= 3; i++ {
|
||||||
|
if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) {
|
||||||
|
t.Errorf("missing cgo symbolizer:%d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -305,6 +305,7 @@ func memlimit() uintptr {
|
|||||||
|
|
||||||
func sigreturn()
|
func sigreturn()
|
||||||
func sigtramp()
|
func sigtramp()
|
||||||
|
func cgoSigtramp()
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:nowritebarrierrec
|
//go:nowritebarrierrec
|
||||||
@ -323,8 +324,12 @@ func setsig(i int32, fn uintptr, restart bool) {
|
|||||||
sa.sa_restorer = funcPC(sigreturn)
|
sa.sa_restorer = funcPC(sigreturn)
|
||||||
}
|
}
|
||||||
if fn == funcPC(sighandler) {
|
if fn == funcPC(sighandler) {
|
||||||
|
if iscgo {
|
||||||
|
fn = funcPC(cgoSigtramp)
|
||||||
|
} else {
|
||||||
fn = funcPC(sigtramp)
|
fn = funcPC(sigtramp)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
sa.sa_handler = fn
|
sa.sa_handler = fn
|
||||||
rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask))
|
rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask))
|
||||||
}
|
}
|
||||||
@ -354,7 +359,7 @@ func getsig(i int32) uintptr {
|
|||||||
if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
|
if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
|
||||||
throw("rt_sigaction read failure")
|
throw("rt_sigaction read failure")
|
||||||
}
|
}
|
||||||
if sa.sa_handler == funcPC(sigtramp) {
|
if sa.sa_handler == funcPC(sigtramp) || sa.sa_handler == funcPC(cgoSigtramp) {
|
||||||
return funcPC(sighandler)
|
return funcPC(sighandler)
|
||||||
}
|
}
|
||||||
return sa.sa_handler
|
return sa.sa_handler
|
||||||
|
@ -375,6 +375,8 @@ type m struct {
|
|||||||
fastrand uint32
|
fastrand uint32
|
||||||
ncgocall uint64 // number of cgo calls in total
|
ncgocall uint64 // number of cgo calls in total
|
||||||
ncgo int32 // number of cgo calls currently in progress
|
ncgo int32 // number of cgo calls currently in progress
|
||||||
|
cgoCallersUse uint32 // if non-zero, cgoCallers in use temporarily
|
||||||
|
cgoCallers *cgoCallers // cgo traceback if crashing in cgo call
|
||||||
park note
|
park note
|
||||||
alllink *m // on allm
|
alllink *m // on allm
|
||||||
schedlink muintptr
|
schedlink muintptr
|
||||||
|
@ -232,6 +232,9 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$12
|
|||||||
CALL runtime·sigtrampgo(SB)
|
CALL runtime·sigtrampgo(SB)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
|
||||||
|
JMP runtime·sigtramp(SB)
|
||||||
|
|
||||||
TEXT runtime·sigreturn(SB),NOSPLIT,$0
|
TEXT runtime·sigreturn(SB),NOSPLIT,$0
|
||||||
MOVL $173, AX // rt_sigreturn
|
MOVL $173, AX // rt_sigreturn
|
||||||
// Sigreturn expects same SP as signal handler,
|
// Sigreturn expects same SP as signal handler,
|
||||||
|
@ -234,8 +234,65 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$24
|
|||||||
CALL AX
|
CALL AX
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
// Used instead of sigtramp in programs that use cgo.
|
||||||
|
// Arguments from kernel are in DI, SI, DX.
|
||||||
|
TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
|
||||||
|
// If no traceback function, do usual sigtramp.
|
||||||
|
MOVQ runtime·cgoTraceback(SB), AX
|
||||||
|
TESTQ AX, AX
|
||||||
|
JZ sigtramp
|
||||||
|
|
||||||
|
// If no traceback support function, which means that
|
||||||
|
// runtime/cgo was not linked in, do usual sigtramp.
|
||||||
|
MOVQ _cgo_callers(SB), AX
|
||||||
|
TESTQ AX, AX
|
||||||
|
JZ sigtramp
|
||||||
|
|
||||||
|
// Figure out if we are currently in a cgo call.
|
||||||
|
// If not, just do usual sigtramp.
|
||||||
|
get_tls(CX)
|
||||||
|
MOVQ g(CX),AX
|
||||||
|
TESTQ AX, AX
|
||||||
|
JZ sigtramp // g == nil
|
||||||
|
MOVQ g_m(AX), AX
|
||||||
|
TESTQ AX, AX
|
||||||
|
JZ sigtramp // g.m == nil
|
||||||
|
MOVL m_ncgo(AX), CX
|
||||||
|
TESTL CX, CX
|
||||||
|
JZ sigtramp // g.m.ncgo == 0
|
||||||
|
MOVQ m_curg(AX), CX
|
||||||
|
TESTQ CX, CX
|
||||||
|
JZ sigtramp // g.m.curg == nil
|
||||||
|
MOVQ g_syscallsp(CX), CX
|
||||||
|
TESTQ CX, CX
|
||||||
|
JZ sigtramp // g.m.curg.syscallsp == 0
|
||||||
|
MOVQ m_cgoCallers(AX), R8
|
||||||
|
TESTQ R8, R8
|
||||||
|
JZ sigtramp // g.m.cgoCallers == nil
|
||||||
|
MOVL m_cgoCallersUse(AX), CX
|
||||||
|
TESTL CX, CX
|
||||||
|
JNZ sigtramp // g.m.cgoCallersUse != 0
|
||||||
|
|
||||||
|
// Jump to a function in runtime/cgo.
|
||||||
|
// That function, written in C, will call the user's traceback
|
||||||
|
// function with proper unwind info, and will then call back here.
|
||||||
|
// The first three arguments are already in registers.
|
||||||
|
// Set the last three arguments now.
|
||||||
|
MOVQ runtime·cgoTraceback(SB), CX
|
||||||
|
MOVQ $runtime·sigtramp(SB), R9
|
||||||
|
MOVQ _cgo_callers(SB), AX
|
||||||
|
JMP AX
|
||||||
|
|
||||||
|
sigtramp:
|
||||||
|
JMP runtime·sigtramp(SB)
|
||||||
|
|
||||||
|
// For cgo unwinding to work, this function must look precisely like
|
||||||
|
// the one in glibc. The glibc source code is:
|
||||||
|
// https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/x86_64/sigaction.c
|
||||||
|
// The code that cares about the precise instructions used is:
|
||||||
|
// https://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/i386/linux-unwind.h?revision=219188&view=markup
|
||||||
TEXT runtime·sigreturn(SB),NOSPLIT,$0
|
TEXT runtime·sigreturn(SB),NOSPLIT,$0
|
||||||
MOVL $15, AX // rt_sigreturn
|
MOVQ $15, AX // rt_sigreturn
|
||||||
SYSCALL
|
SYSCALL
|
||||||
INT $3 // not reached
|
INT $3 // not reached
|
||||||
|
|
||||||
|
@ -361,6 +361,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$12
|
|||||||
BL (R11)
|
BL (R11)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
|
||||||
|
MOVW $runtime·sigtramp(SB), R11
|
||||||
|
B (R11)
|
||||||
|
|
||||||
TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
|
TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
|
||||||
MOVW sig+0(FP), R0
|
MOVW sig+0(FP), R0
|
||||||
MOVW new+4(FP), R1
|
MOVW new+4(FP), R1
|
||||||
|
@ -259,6 +259,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$24
|
|||||||
BL (R0)
|
BL (R0)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
|
||||||
|
MOVD $runtime·sigtramp(SB), R3
|
||||||
|
B (R3)
|
||||||
|
|
||||||
TEXT runtime·mmap(SB),NOSPLIT,$-8
|
TEXT runtime·mmap(SB),NOSPLIT,$-8
|
||||||
MOVD addr+0(FP), R0
|
MOVD addr+0(FP), R0
|
||||||
MOVD n+8(FP), R1
|
MOVD n+8(FP), R1
|
||||||
|
@ -249,6 +249,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64
|
|||||||
JAL (R1)
|
JAL (R1)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
|
||||||
|
MOVV $runtime·sigtramp(SB), R1
|
||||||
|
JMP (R1)
|
||||||
|
|
||||||
TEXT runtime·mmap(SB),NOSPLIT,$-8
|
TEXT runtime·mmap(SB),NOSPLIT,$-8
|
||||||
MOVV addr+0(FP), R4
|
MOVV addr+0(FP), R4
|
||||||
MOVV n+8(FP), R5
|
MOVV n+8(FP), R5
|
||||||
|
@ -243,6 +243,21 @@ TEXT runtime·_sigtramp(SB),NOSPLIT,$64
|
|||||||
MOVD 24(R1), R2
|
MOVD 24(R1), R2
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
#ifdef GOARCH_ppc64le
|
||||||
|
// ppc64le doesn't need function descriptors
|
||||||
|
TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
|
||||||
|
#else
|
||||||
|
// function descriptor for the real sigtramp
|
||||||
|
TEXT runtime·cgoSigtramp(SB),NOSPLIT|NOFRAME,$0
|
||||||
|
DWORD $runtime·_cgoSigtramp(SB)
|
||||||
|
DWORD $0
|
||||||
|
DWORD $0
|
||||||
|
TEXT runtime·_cgoSigtramp(SB),NOSPLIT,$0
|
||||||
|
#endif
|
||||||
|
MOVD $runtime·sigtramp(SB), R12
|
||||||
|
MOVD R12, CTR
|
||||||
|
JMP (CTR)
|
||||||
|
|
||||||
TEXT runtime·mmap(SB),NOSPLIT|NOFRAME,$0
|
TEXT runtime·mmap(SB),NOSPLIT|NOFRAME,$0
|
||||||
MOVD addr+0(FP), R3
|
MOVD addr+0(FP), R3
|
||||||
MOVD n+8(FP), R4
|
MOVD n+8(FP), R4
|
||||||
|
80
src/runtime/testdata/testprogcgo/traceback.go
vendored
Normal file
80
src/runtime/testdata/testprogcgo/traceback.go
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// This program will crash.
|
||||||
|
// We want the stack trace to include the C functions.
|
||||||
|
// We use a fake traceback, and a symbolizer that dumps a string we recognize.
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -g -O0
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
static int f3() {
|
||||||
|
*p = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int f2() {
|
||||||
|
return f3();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int f1() {
|
||||||
|
return f2();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cgoTracebackArg {
|
||||||
|
uintptr_t context;
|
||||||
|
uintptr_t* buf;
|
||||||
|
uintptr_t max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cgoSymbolizerArg {
|
||||||
|
uintptr_t pc;
|
||||||
|
const char* file;
|
||||||
|
uintptr_t lineno;
|
||||||
|
const char* func;
|
||||||
|
uintptr_t entry;
|
||||||
|
uintptr_t more;
|
||||||
|
uintptr_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void cgoTraceback(void* parg) {
|
||||||
|
struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg);
|
||||||
|
arg->buf[0] = 1;
|
||||||
|
arg->buf[1] = 2;
|
||||||
|
arg->buf[2] = 3;
|
||||||
|
arg->buf[3] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cgoSymbolizer(void* parg) {
|
||||||
|
struct cgoSymbolizerArg* arg = (struct cgoSymbolizerArg*)(parg);
|
||||||
|
if (arg->pc != arg->data + 1) {
|
||||||
|
arg->file = "unexpected data";
|
||||||
|
} else {
|
||||||
|
arg->file = "cgo symbolizer";
|
||||||
|
}
|
||||||
|
arg->lineno = arg->data + 1;
|
||||||
|
arg->data++;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
register("CrashTraceback", CrashTraceback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CrashTraceback() {
|
||||||
|
runtime.SetCgoTraceback(0, unsafe.Pointer(C.cgoTraceback), nil, unsafe.Pointer(C.cgoSymbolizer))
|
||||||
|
C.f1()
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"runtime/internal/atomic"
|
||||||
"runtime/internal/sys"
|
"runtime/internal/sys"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -579,6 +580,22 @@ func tracebacktrap(pc, sp, lr uintptr, gp *g) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func traceback1(pc, sp, lr uintptr, gp *g, flags uint) {
|
func traceback1(pc, sp, lr uintptr, gp *g, flags uint) {
|
||||||
|
// If the goroutine is in cgo, and we have a cgo traceback, print that.
|
||||||
|
if iscgo && gp.m != nil && gp.m.ncgo > 0 && gp.syscallsp != 0 && gp.m.cgoCallers != nil && gp.m.cgoCallers[0] != 0 {
|
||||||
|
// Lock cgoCallers so that a signal handler won't
|
||||||
|
// change it, copy the array, reset it, unlock it.
|
||||||
|
// We are locked to the thread and are not running
|
||||||
|
// concurrently with a signal handler.
|
||||||
|
// We just have to stop a signal handler from interrupting
|
||||||
|
// in the middle of our copy.
|
||||||
|
atomic.Store(&gp.m.cgoCallersUse, 1)
|
||||||
|
cgoCallers := *gp.m.cgoCallers
|
||||||
|
gp.m.cgoCallers[0] = 0
|
||||||
|
atomic.Store(&gp.m.cgoCallersUse, 0)
|
||||||
|
|
||||||
|
printCgoTraceback(&cgoCallers)
|
||||||
|
}
|
||||||
|
|
||||||
var n int
|
var n int
|
||||||
if readgstatus(gp)&^_Gscan == _Gsyscall {
|
if readgstatus(gp)&^_Gscan == _Gsyscall {
|
||||||
// Override registers if blocked in system call.
|
// Override registers if blocked in system call.
|
||||||
@ -739,3 +756,233 @@ func isSystemGoroutine(gp *g) bool {
|
|||||||
pc == timerprocPC ||
|
pc == timerprocPC ||
|
||||||
pc == gcBgMarkWorkerPC
|
pc == gcBgMarkWorkerPC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCgoTraceback records three C functions to use to gather
|
||||||
|
// traceback information from C code and to convert that traceback
|
||||||
|
// information into symbolic information. These are used when printing
|
||||||
|
// stack traces for a program that uses cgo.
|
||||||
|
//
|
||||||
|
// The traceback and context functions may be called from a signal
|
||||||
|
// handler, and must therefore use only async-signal safe functions.
|
||||||
|
// The symbolizer function may be called while the program is
|
||||||
|
// crashing, and so must be cautious about using memory. None of the
|
||||||
|
// functions may call back into Go.
|
||||||
|
//
|
||||||
|
// The context function will be called with a single argument, a
|
||||||
|
// pointer to a struct:
|
||||||
|
//
|
||||||
|
// struct {
|
||||||
|
// Context uintptr
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// In C syntax, this struct will be
|
||||||
|
//
|
||||||
|
// struct {
|
||||||
|
// uintptr_t Context;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// If the Context field is 0, the context function is being called to
|
||||||
|
// record the current traceback context. It should record whatever
|
||||||
|
// information is needed about the current point of execution to later
|
||||||
|
// produce a stack trace, probably the stack pointer and PC. In this
|
||||||
|
// case the context function will be called from C code.
|
||||||
|
//
|
||||||
|
// If the Context field is not 0, then it is a value returned by a
|
||||||
|
// previous call to the context function. This case is called when the
|
||||||
|
// context is no longer needed; that is, when the Go code is returning
|
||||||
|
// to its C code caller. This permits permits the context function to
|
||||||
|
// release any associated resources.
|
||||||
|
//
|
||||||
|
// While it would be correct for the context function to record a
|
||||||
|
// complete a stack trace whenever it is called, and simply copy that
|
||||||
|
// out in the traceback function, in a typical program the context
|
||||||
|
// function will be called many times without ever recording a
|
||||||
|
// traceback for that context. Recording a complete stack trace in a
|
||||||
|
// call to the context function is likely to be inefficient.
|
||||||
|
//
|
||||||
|
// The traceback function will be called with a single argument, a
|
||||||
|
// pointer to a struct:
|
||||||
|
//
|
||||||
|
// struct {
|
||||||
|
// Context uintptr
|
||||||
|
// Buf *uintptr
|
||||||
|
// Max uintptr
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// In C syntax, this struct will be
|
||||||
|
//
|
||||||
|
// struct {
|
||||||
|
// uintptr_t Context;
|
||||||
|
// uintptr_t* Buf;
|
||||||
|
// uintptr_t Max;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// The Context field will be zero to gather a traceback from the
|
||||||
|
// current program execution point. In this case, the traceback
|
||||||
|
// function will be called from C code.
|
||||||
|
//
|
||||||
|
// Otherwise Context will be a value previously returned by a call to
|
||||||
|
// the context function. The traceback function should gather a stack
|
||||||
|
// trace from that saved point in the program execution. The traceback
|
||||||
|
// function may be called from an execution thread other than the one
|
||||||
|
// that recorded the context, but only when the context is known to be
|
||||||
|
// valid and unchanging. The traceback function may also be called
|
||||||
|
// deeper in the call stack on the same thread that recorded the
|
||||||
|
// context. The traceback function may be called multiple times with
|
||||||
|
// the same Context value; it will usually be appropriate to cache the
|
||||||
|
// result, if possible, the first time this is called for a specific
|
||||||
|
// context value.
|
||||||
|
//
|
||||||
|
// Buf is where the traceback information should be stored. It should
|
||||||
|
// be PC values, such that Buf[0] is the PC of the caller, Buf[1] is
|
||||||
|
// the PC of that function's caller, and so on. Max is the maximum
|
||||||
|
// number of entries to store. The function should store a zero to
|
||||||
|
// indicate the top of the stack, or that the caller is on a different
|
||||||
|
// stack, presumably a Go stack.
|
||||||
|
//
|
||||||
|
// Unlike runtime.Callers, the PC values returned should, when passed
|
||||||
|
// to the symbolizer function, return the file/line of the call
|
||||||
|
// instruction. No additional subtraction is required or appropriate.
|
||||||
|
//
|
||||||
|
// The symbolizer function will be called with a single argument, a
|
||||||
|
// pointer to a struct:
|
||||||
|
//
|
||||||
|
// struct {
|
||||||
|
// PC uintptr // program counter to fetch information for
|
||||||
|
// File *byte // file name (NUL terminated)
|
||||||
|
// Lineno uintptr // line number
|
||||||
|
// Func *byte // function name (NUL terminated)
|
||||||
|
// Entry uintptr // function entry point
|
||||||
|
// More uintptr // set non-zero if more info for this PC
|
||||||
|
// Data uintptr // unused by runtime, available for function
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// In C syntax, this struct will be
|
||||||
|
//
|
||||||
|
// struct {
|
||||||
|
// uintptr_t PC;
|
||||||
|
// char* File;
|
||||||
|
// uintptr_t Lineno;
|
||||||
|
// char* Func;
|
||||||
|
// uintptr_t Entry;
|
||||||
|
// uintptr_t More;
|
||||||
|
// uintptr_t Data;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// The PC field will be a value returned by a call to the traceback
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// The first time the function is called for a particular traceback,
|
||||||
|
// all the fields except PC will be 0. The function should fill in the
|
||||||
|
// other fields if possible, setting them to 0/nil if the information
|
||||||
|
// is not available. The Data field may be used to store any useful
|
||||||
|
// information across calls. The More field should be set to non-zero
|
||||||
|
// if there is more information for this PC, zero otherwise. If More
|
||||||
|
// is set non-zero, the function will be called again with the same
|
||||||
|
// PC, and may return different information (this is intended for use
|
||||||
|
// with inlined functions). If More is zero, the function will be
|
||||||
|
// called with the next PC value in the traceback. When the traceback
|
||||||
|
// is complete, the function will be called once more with PC set to
|
||||||
|
// zero; this may be used to free any information. Each call will
|
||||||
|
// leave the fields of the struct set to the same values they had upon
|
||||||
|
// return, except for the PC field when the More field is zero. The
|
||||||
|
// function must not keep a copy of the struct pointer between calls.
|
||||||
|
//
|
||||||
|
// When calling SetCgoTraceback, the version argument is the version
|
||||||
|
// number of the structs that the functions expect to receive.
|
||||||
|
// Currently this must be zero.
|
||||||
|
//
|
||||||
|
// The symbolizer function may be nil, in which case the results of
|
||||||
|
// the traceback function will be displayed as numbers. If the
|
||||||
|
// traceback function is nil, the symbolizer function will never be
|
||||||
|
// called. The context function may be nil, in which case the
|
||||||
|
// traceback function will only be called with the context field set
|
||||||
|
// to zero. If the context function is nil, then calls from Go to C
|
||||||
|
// to Go will not show a traceback for the C portion of the call stack.
|
||||||
|
func SetCgoTraceback(version int, traceback, context, symbolizer unsafe.Pointer) {
|
||||||
|
if version != 0 {
|
||||||
|
panic("unsupported version")
|
||||||
|
}
|
||||||
|
if context != nil {
|
||||||
|
panic("SetCgoTraceback: context function not yet implemented")
|
||||||
|
}
|
||||||
|
cgoTraceback = traceback
|
||||||
|
cgoContext = context
|
||||||
|
cgoSymbolizer = symbolizer
|
||||||
|
}
|
||||||
|
|
||||||
|
var cgoTraceback unsafe.Pointer
|
||||||
|
var cgoContext unsafe.Pointer
|
||||||
|
var cgoSymbolizer unsafe.Pointer
|
||||||
|
|
||||||
|
// cgoTracebackArg is the type passed to cgoTraceback.
|
||||||
|
type cgoTracebackArg struct {
|
||||||
|
context uintptr
|
||||||
|
buf *uintptr
|
||||||
|
max uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// cgoContextArg is the type passed to cgoContext.
|
||||||
|
type cgoContextArg struct {
|
||||||
|
context uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// cgoSymbolizerArg is the type passed to cgoSymbolizer.
|
||||||
|
type cgoSymbolizerArg struct {
|
||||||
|
pc uintptr
|
||||||
|
file *byte
|
||||||
|
lineno uintptr
|
||||||
|
funcName *byte
|
||||||
|
entry uintptr
|
||||||
|
more uintptr
|
||||||
|
data uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// cgoTraceback prints a traceback of callers.
|
||||||
|
func printCgoTraceback(callers *cgoCallers) {
|
||||||
|
if cgoSymbolizer == nil {
|
||||||
|
for _, c := range callers {
|
||||||
|
if c == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
print("non-Go function at pc=", hex(c), "\n")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
call := cgocall
|
||||||
|
if panicking > 0 {
|
||||||
|
// We do not want to call into the scheduler when panicking.
|
||||||
|
call = asmcgocall
|
||||||
|
}
|
||||||
|
|
||||||
|
var arg cgoSymbolizerArg
|
||||||
|
for _, c := range callers {
|
||||||
|
if c == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
arg.pc = c
|
||||||
|
for {
|
||||||
|
call(cgoSymbolizer, noescape(unsafe.Pointer(&arg)))
|
||||||
|
if arg.funcName != nil {
|
||||||
|
// Note that we don't print any argument
|
||||||
|
// information here, not even parentheses.
|
||||||
|
// The symbolizer must add that if
|
||||||
|
// appropriate.
|
||||||
|
println(gostringnocopy(arg.funcName))
|
||||||
|
} else {
|
||||||
|
println("non-Go function")
|
||||||
|
}
|
||||||
|
print("\t")
|
||||||
|
if arg.file != nil {
|
||||||
|
print(gostringnocopy(arg.file), ":", arg.lineno, " ")
|
||||||
|
}
|
||||||
|
print("pc=", hex(c), "\n")
|
||||||
|
if arg.more == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg.pc = 0
|
||||||
|
call(cgoSymbolizer, noescape(unsafe.Pointer(&arg)))
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user