2022-05-08 19:49:45 +02:00
|
|
|
package errors
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Callers is a stacktrace of caller PCs.
|
|
|
|
type Callers []uintptr
|
|
|
|
|
|
|
|
// GetCallers returns a Callers slice of PCs, of at most 'depth'.
|
|
|
|
func GetCallers(skip int, depth int) Callers {
|
|
|
|
rpc := make([]uintptr, depth)
|
|
|
|
n := runtime.Callers(skip+1, rpc)
|
|
|
|
return Callers(rpc[0:n])
|
|
|
|
}
|
|
|
|
|
|
|
|
// Frames fetches runtime frames for a slice of caller PCs.
|
|
|
|
func (f Callers) Frames() []runtime.Frame {
|
|
|
|
// Allocate expected frames slice
|
|
|
|
frames := make([]runtime.Frame, 0, len(f))
|
|
|
|
|
|
|
|
// Get frames iterator for PCs
|
|
|
|
iter := runtime.CallersFrames(f)
|
|
|
|
|
|
|
|
for {
|
|
|
|
// Get next frame in iter
|
|
|
|
frame, ok := iter.Next()
|
|
|
|
if !ok {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append to frames slice
|
|
|
|
frames = append(frames, frame)
|
|
|
|
}
|
|
|
|
|
|
|
|
return frames
|
|
|
|
}
|
|
|
|
|
2023-01-17 12:25:13 +01:00
|
|
|
// MarshalJSON implements json.Marshaler to provide an easy, simple default.
|
2022-05-08 19:49:45 +02:00
|
|
|
func (f Callers) MarshalJSON() ([]byte, error) {
|
|
|
|
// JSON-able frame type
|
2023-01-17 12:25:13 +01:00
|
|
|
type jsonFrame struct {
|
2022-05-08 19:49:45 +02:00
|
|
|
Func string `json:"func"`
|
|
|
|
File string `json:"file"`
|
|
|
|
Line int `json:"line"`
|
|
|
|
}
|
|
|
|
|
2023-01-17 12:25:13 +01:00
|
|
|
// Convert to frames
|
|
|
|
frames := f.Frames()
|
2022-05-08 19:49:45 +02:00
|
|
|
|
2023-01-17 12:25:13 +01:00
|
|
|
// Allocate expected size jsonFrame slice
|
|
|
|
jsonFrames := make([]jsonFrame, 0, len(f))
|
2022-05-08 19:49:45 +02:00
|
|
|
|
2023-01-17 12:25:13 +01:00
|
|
|
for i := 0; i < len(frames); i++ {
|
|
|
|
frame := frames[i]
|
2022-05-08 19:49:45 +02:00
|
|
|
|
2023-01-17 12:25:13 +01:00
|
|
|
// Convert each to jsonFrame object
|
|
|
|
jsonFrames = append(jsonFrames, jsonFrame{
|
|
|
|
Func: funcname(frame.Function),
|
|
|
|
File: frame.File,
|
|
|
|
Line: frame.Line,
|
2022-05-08 19:49:45 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// marshal converted frames
|
|
|
|
return json.Marshal(frames)
|
|
|
|
}
|
|
|
|
|
|
|
|
// String will return a simple string representation of receiving Callers slice.
|
|
|
|
func (f Callers) String() string {
|
|
|
|
// Guess-timate to reduce allocs
|
|
|
|
buf := make([]byte, 0, 64*len(f))
|
|
|
|
|
|
|
|
// Convert to frames
|
|
|
|
frames := f.Frames()
|
|
|
|
|
|
|
|
for i := 0; i < len(frames); i++ {
|
|
|
|
frame := frames[i]
|
|
|
|
|
|
|
|
// Append formatted caller info
|
2023-01-17 12:25:13 +01:00
|
|
|
fn := funcname(frame.Function)
|
|
|
|
buf = append(buf, fn+"()\n\t"+frame.File+":"...)
|
2022-05-08 19:49:45 +02:00
|
|
|
buf = strconv.AppendInt(buf, int64(frame.Line), 10)
|
|
|
|
buf = append(buf, '\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
return *(*string)(unsafe.Pointer(&buf))
|
|
|
|
}
|
|
|
|
|
|
|
|
// funcname splits a function name with pkg from its path prefix.
|
|
|
|
func funcname(name string) string {
|
|
|
|
i := strings.LastIndex(name, "/")
|
|
|
|
return name[i+1:]
|
|
|
|
}
|