| package main |
| |
| import ( |
| "fmt" |
| "path" |
| "runtime" |
| "strings" |
| ) |
| |
| var stack string |
| |
| func f() { |
| pc := make([]uintptr, 6) |
| pc = pc[:runtime.Callers(1, pc)] |
| for _, f := range pc { |
| Func := runtime.FuncForPC(f) |
| name := Func.Name() |
| if strings.Contains(name, "$") || strings.Contains(name, ".func") { |
| name = "func" // anon funcs vary across toolchains |
| } |
| file, line := Func.FileLine(0) |
| stack += fmt.Sprintf("%s at %s:%d\n", name, path.Base(file), line) |
| } |
| } |
| |
| func g() { f() } |
| func h() { g() } |
| func i() { func() { h() }() } |
| |
| // Hack: the 'func' and the call to Caller are on the same line, |
| // to paper over differences between toolchains. |
| // (The interpreter's location info isn't yet complete.) |
| func runtimeCaller0() (uintptr, string, int, bool) { return runtime.Caller(0) } |
| |
| func main() { |
| i() |
| if stack != `main.f at callstack.go:12 |
| main.g at callstack.go:26 |
| main.h at callstack.go:27 |
| func at callstack.go:28 |
| main.i at callstack.go:28 |
| main.main at callstack.go:35 |
| ` { |
| panic("unexpected stack: " + stack) |
| } |
| |
| pc, file, line, _ := runtimeCaller0() |
| got := fmt.Sprintf("%s @ %s:%d", runtime.FuncForPC(pc).Name(), path.Base(file), line) |
| if got != "main.runtimeCaller0 @ callstack.go:33" { |
| panic("runtime.Caller: " + got) |
| } |
| } |