105 lines
2.5 KiB
Go
105 lines
2.5 KiB
Go
|
// Package debugging contains useful methods for debugging the app, safely
|
||
|
// isolated from the rest of the app's packages.
|
||
|
package debugging
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// Configurable variables for the stack tracer functions.
|
||
|
var (
|
||
|
// StackDepth is the depth that Callers() will crawl up the call stack. This
|
||
|
// variable is configurable.
|
||
|
StackDepth = 20
|
||
|
|
||
|
// StopAt is the function name to stop the tracebacks at. Set to a blank
|
||
|
// string to not stop and trace all the way up to `runtime.goexit` or
|
||
|
// wherever.
|
||
|
StopAt = "main.main"
|
||
|
)
|
||
|
|
||
|
// Minimum depth given to runtime.Caller() so that the call stacks will exclude
|
||
|
// the call to debugging.Caller() itself -- so this debug module won't debug its
|
||
|
// own function calls in the tracebacks.
|
||
|
const minDepth = 2
|
||
|
|
||
|
// Caller returns the filename and line number that called the calling
|
||
|
// function.
|
||
|
func Caller() string {
|
||
|
if pc, file, no, ok := runtime.Caller(minDepth); ok {
|
||
|
frames := runtime.CallersFrames([]uintptr{pc})
|
||
|
frame, _ := frames.Next()
|
||
|
if frame.Function != "" {
|
||
|
return fmt.Sprintf("%s#%d: %s()",
|
||
|
frame.File,
|
||
|
frame.Line,
|
||
|
frame.Function,
|
||
|
)
|
||
|
}
|
||
|
return fmt.Sprintf("%s#%d",
|
||
|
file,
|
||
|
no,
|
||
|
)
|
||
|
}
|
||
|
return "[no caller information]"
|
||
|
}
|
||
|
|
||
|
// Callers returns an array of all the callers of the current function.
|
||
|
func Callers() []string {
|
||
|
var (
|
||
|
callers []string
|
||
|
pc = make([]uintptr, StackDepth)
|
||
|
count = runtime.Callers(minDepth, pc)
|
||
|
)
|
||
|
pc = pc[:count] // only pass valid program counters to CallersFrames
|
||
|
var frames = runtime.CallersFrames(pc)
|
||
|
_ = frames
|
||
|
|
||
|
// Loop to get frames of the call stack.
|
||
|
for {
|
||
|
frame, more := frames.Next()
|
||
|
|
||
|
callers = append(callers, fmt.Sprintf("%s#%d: %s()",
|
||
|
frame.File,
|
||
|
frame.Line,
|
||
|
frame.Function,
|
||
|
))
|
||
|
|
||
|
if StopAt != "" && frame.Function == StopAt {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
if !more {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return callers
|
||
|
}
|
||
|
|
||
|
// StringifyCallers pretty-prints the Callers as a single string with newlines.
|
||
|
func StringifyCallers() string {
|
||
|
callers := Callers()
|
||
|
var result []string
|
||
|
for i, caller := range callers {
|
||
|
if i == 0 {
|
||
|
continue // StringifyCallers() would be the first row, skip it.
|
||
|
}
|
||
|
result = append(result, fmt.Sprintf("%d: %s", i, caller))
|
||
|
}
|
||
|
return strings.Join(result, "\n")
|
||
|
}
|
||
|
|
||
|
// PrintCallers prints the stringified callers directly to STDOUT.
|
||
|
func PrintCallers() {
|
||
|
fmt.Println("Call stack (most recent/current function first):")
|
||
|
for i, caller := range Callers() {
|
||
|
if i == 0 {
|
||
|
continue // PrintCallers() would be the first row, skip it.
|
||
|
}
|
||
|
fmt.Printf("%d: %s\n", i, caller)
|
||
|
}
|
||
|
}
|