GoToSocial/vendor/codeberg.org/gruf/go-errors/v2/callers.go
tobi 5004e0a9da
[bugfix] Fix remote media pruning failing if media already gone (#548)
* fix error check of prune to allow missing files

* update go-store library, add test for pruning item with db entry but no file

Signed-off-by: kim <grufwub@gmail.com>

* remove now-unneccessary error check

Signed-off-by: kim <grufwub@gmail.com>

Co-authored-by: kim <grufwub@gmail.com>
2022-05-08 18:49:45 +01:00

103 lines
2.1 KiB
Go

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
}
// MarshalJSON implements json.Marshaler to provide an easy, simply default.
func (f Callers) MarshalJSON() ([]byte, error) {
// JSON-able frame type
type frame struct {
Func string `json:"func"`
File string `json:"file"`
Line int `json:"line"`
}
// Allocate expected frames slice
frames := make([]frame, 0, len(f))
// Get frames iterator for PCs
iter := runtime.CallersFrames(f)
for {
// Get next frame
f, ok := iter.Next()
if !ok {
break
}
// Append to frames slice
frames = append(frames, frame{
Func: funcname(f.Function),
File: f.File,
Line: f.Line,
})
}
// 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
funcname := funcname(frame.Function)
buf = append(buf, funcname+"()\n\t"+frame.File+":"...)
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:]
}