-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 9f05ade
Showing
8 changed files
with
267 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# cgosymbolizer | ||
|
||
[![GoDoc](https://godoc.org/github.com/benesch/cgosymbolizer?status.svg)](https://godoc.org/github.com/benesch/cgosymbolizer) | ||
|
||
cgosymbolizer teaches the Go runtime to include cgo frames in backtraces. | ||
|
||
## Usage | ||
|
||
Just import the package for its side effects: | ||
|
||
```go | ||
import _ github.com/benesch/cgosymbolizer | ||
``` | ||
|
||
## Example output | ||
|
||
When you get a backtrace from a Go function that's called from a C function, | ||
you'll see the C frames in the backtrace: | ||
|
||
``` | ||
./example | ||
goroutine 1 [running, locked to thread]: | ||
runtime/debug.Stack(0x405ac8d, 0x8, 0xc000000180) | ||
/Users/benesch/Sites/go/src/runtime/debug/stack.go:24 +0xa7 | ||
runtime/debug.PrintStack() | ||
/Users/benesch/Sites/go/src/runtime/debug/stack.go:16 +0x22 | ||
main.goPrintStack() | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/example.go:32 +0x20 | ||
main._cgoexpwrap_0981d921ddb7_goPrintStack() | ||
_cgo_gotypes.go:57 +0x20 | ||
goPrintStack | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/./example:0 pc=0x407f4e5 | ||
c3 | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/./example:0 pc=0x407f519 | ||
c2 | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/./example:0 pc=0x407f529 | ||
c1 | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/./example:0 pc=0x407f539 | ||
main._Cfunc_c1() | ||
_cgo_gotypes.go:41 +0x41 | ||
main.go2() | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/example.go:27 +0x20 | ||
main.go1() | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/example.go:23 +0x20 | ||
main.main() | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/example.go:19 +0x20 | ||
``` | ||
|
||
Without cgosymbolizer, c1, c2, and c3 would be missing. | ||
|
||
You'll also see C frames in backtraces generated due to fatal signals. Here's an | ||
example: | ||
|
||
``` | ||
goroutine 1 [syscall]: | ||
x_cgo_callers | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/./example:0 pc=0x407fb83 | ||
abort | ||
/usr/lib/system/libsystem_c.dylib:0 pc=0x7fffb9b2d420 | ||
c3 | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/./example:0 pc=0x407f51e | ||
c2 | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/./example:0 pc=0x407f529 | ||
c1 | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/./example:0 pc=0x407f539 | ||
runtime.cgocall(0x407f500, 0xc000067f58, 0xc00001e118) | ||
/Users/benesch/Sites/go/src/runtime/cgocall.go:128 +0x65 fp=0xc000067f28 sp=0xc000067ef0 pc=0x40043f5 | ||
main._Cfunc_c1() | ||
_cgo_gotypes.go:41 +0x41 fp=0xc000067f58 sp=0xc000067f28 pc=0x407f2e1 | ||
main.go2() | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/example.go:27 +0x20 fp=0xc000067f68 sp=0xc000067f58 pc=0x407f410 | ||
main.go1() | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/example.go:23 +0x20 fp=0xc000067f78 sp=0xc000067f68 pc=0x407f3e0 | ||
main.main() | ||
/Users/benesch/go/src/github.com/benesch/cgosymbolizer/example/example.go:19 +0x20 fp=0xc000067f88 sp=0xc000067f78 pc=0x407f3b0 | ||
runtime.main() | ||
/Users/benesch/Sites/go/src/runtime/proc.go:198 +0x207 fp=0xc000067fe0 sp=0xc000067f88 pc=0x402a417 | ||
runtime.goexit() | ||
/Users/benesch/Sites/go/src/runtime/asm_amd64.s:1385 +0x1 fp=0xc000067fe8 sp=0xc000067fe0 pc=0x40501d1 | ||
``` | ||
|
||
# Platform support | ||
|
||
Only Linux and macOS are supported. | ||
|
||
Collecting C stack traces when C code receives a signal—the second example | ||
above—requires additional support from the Go runtime. Go 1.10 only provides | ||
this support for linux/amd64 and linux/ppc64le. Go 1.11 will add support for | ||
freebsd/amd64, and possibly darwin/amd64. For details, follow [golang/go#24518]. | ||
|
||
[golang/go#24518]: https://github.com/golang/go/issues/24518 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#include <dlfcn.h> | ||
#include <libunwind.h> | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include "cgosymbolizer_darwin.h" | ||
|
||
struct cgo_context { | ||
unw_context_t unw_ctx; | ||
unw_cursor_t unw_cursor; | ||
}; | ||
|
||
void cgo_traceback(void* p) { | ||
struct { | ||
uintptr_t ctx; | ||
uintptr_t sig_ctx; | ||
uintptr_t* buf; | ||
uintptr_t max; | ||
}* arg = p; | ||
|
||
struct cgo_context ctx; | ||
if (arg->ctx) | ||
ctx = *(struct cgo_context*) arg->ctx; | ||
else { | ||
if (unw_getcontext(&ctx.unw_ctx) != 0) | ||
return; | ||
if (unw_init_local(&ctx.unw_cursor, &ctx.unw_ctx) != 0) | ||
return; | ||
} | ||
|
||
int i = 0; | ||
while (i < arg->max && unw_step(&ctx.unw_cursor) > 0) { | ||
unw_word_t pc; | ||
unw_get_reg(&ctx.unw_cursor, UNW_REG_IP, &pc); | ||
if (pc == 0) | ||
break; | ||
arg->buf[i++] = pc; | ||
} | ||
if (i < arg->max) | ||
arg->buf[i] = 0; | ||
} | ||
|
||
void cgo_context(void* p) { | ||
struct { | ||
uintptr_t ctx; | ||
}* arg = p; | ||
|
||
if (arg->ctx != 0) { | ||
free((struct cgo_context*) arg->ctx); | ||
return; | ||
} | ||
|
||
struct cgo_context* ctx = malloc(sizeof(cgo_context)); | ||
if (ctx == NULL) | ||
return; | ||
if (unw_getcontext(&ctx->unw_ctx) != 0) | ||
return; | ||
if (unw_init_local(&ctx->unw_cursor, &ctx->unw_ctx) != 0) | ||
return; | ||
// Step the saved state past this frame. It will cease to exist momentarily. | ||
if (unw_step(&ctx->unw_cursor) <= 0) | ||
return; | ||
arg->ctx = (uintptr_t) ctx; | ||
} | ||
|
||
void cgo_symbolizer(void* p) { | ||
struct { | ||
uintptr_t pc; | ||
const char* file; | ||
uintptr_t lineno; | ||
const char* func; | ||
uintptr_t entry; | ||
uintptr_t more; | ||
uintptr_t data; | ||
}* arg = p; | ||
|
||
Dl_info dlinfo; | ||
if (dladdr((void *) arg->pc, &dlinfo) == 0) { | ||
arg->file = "?"; | ||
arg->func = "?"; | ||
arg->entry = 0; | ||
return; | ||
} | ||
arg->file = dlinfo.dli_fname; | ||
arg->func = dlinfo.dli_sname; | ||
arg->entry = (uintptr_t) dlinfo.dli_saddr; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package cgosymbolizer | ||
|
||
import ( | ||
"runtime" | ||
) | ||
|
||
// #include "cgosymbolizer_darwin.h" | ||
import "C" | ||
|
||
func init() { | ||
const structVersion = 0 | ||
runtime.SetCgoTraceback(structVersion, C.cgo_traceback, C.cgo_context, C.cgo_symbolizer) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
void cgo_traceback(void* p); | ||
void cgo_context(void* p); | ||
void cgo_symbolizer(void* p); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package cgosymbolizer | ||
|
||
import _ "github.com/ianlancetaylor/cgosymbolizer" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Package cgosymbolizer teaches the Go runtime to include cgo frames in | ||
// backtraces. | ||
// | ||
// To use the package, import it for its side effects: | ||
// | ||
// import _ github.com/benesch/cgosymbolizer | ||
// | ||
// cgosymbolizer only supports Linux and macOS. Importing it on another | ||
// operating system will have no effect. | ||
// | ||
// Support is further limited by Go's implementation of runtime.SetCgoTraceback. | ||
// On at least linux/amd64 and linux/ppc64le, Go can collect a C traceback, with | ||
// cgosymbolizer's help, if the C code receives a signal. This permits | ||
// visibility into C code in CPU profiles and fatal signals. Otherwise | ||
// cgosymbolizer can only collect a C stack trace when a C function calls a Go | ||
// function. For details, follow https://github.com/golang/go/issues/24518. | ||
// | ||
// Note that the Linux implementation is provided by | ||
// github.com/ianlancetaylor/cgosymbolizer. The macOS implementation is original | ||
// work. | ||
package cgosymbolizer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#include <stdlib.h> | ||
|
||
void goPrintStack(void); | ||
|
||
void c3(void) { | ||
goPrintStack(); | ||
abort(); | ||
} | ||
|
||
void c2(void) { | ||
c3(); | ||
} | ||
|
||
void c1(void) { | ||
c2(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Example demonstrates the use of github.com/benesch/cgosymbolizer. | ||
// | ||
// When invoked it prints two backtraces that include C functions. The first is | ||
// generated by a C function that calls a Go function which dumps the stack. The | ||
// second is generated by raising SIGABRT from C. | ||
package main | ||
|
||
import ( | ||
"runtime/debug" | ||
|
||
_ "github.com/benesch/cgosymbolizer" | ||
) | ||
|
||
// #cgo CFLAGS: -g | ||
// void c1(void); | ||
import "C" | ||
|
||
func main() { | ||
go1() | ||
} | ||
|
||
func go1() { | ||
go2() | ||
} | ||
|
||
func go2() { | ||
C.c1() | ||
} | ||
|
||
//export goPrintStack | ||
func goPrintStack() { | ||
debug.PrintStack() | ||
} |