Files
gaze-indexer/pkg/stacktrace/stacktrace.go
2024-04-10 09:13:33 +07:00

93 lines
2.3 KiB
Go

package stacktrace
import (
"fmt"
"io"
"runtime"
"strings"
)
// StackTrace is the type of the data for a call stack.
// This mirrors the type of the same name in [github.com/cockroachdb/errors/errbase.StackTrace].
type StackTrace struct {
PCS []uintptr
Frames []Frame
}
// Caller captures a stack trace of the specified depth, skipping
// the provided number of frames. skip=0 identifies the caller of Caller.
//
// Alias of [Capture]
func Caller(skip int) *StackTrace {
return Capture(1 + skip)
}
// Capture captures a stack trace of the specified depth, skipping
// the provided number of frames. skip=0 identifies the caller of Capture.
func Capture(skip int) *StackTrace {
pcs := make([]uintptr, 64)
n := runtime.Callers(2+skip, pcs[:])
// Expand the pcs slice if there wasn't enough room.
for n == len(pcs) {
pcs = make([]uintptr, 2*len(pcs))
n = runtime.Callers(2+skip, pcs[:])
}
// Deallocate the unused space in the slice.
pcs = pcs[:n:n]
return ParsePCS(pcs)
}
func ParsePCS(pcs []uintptr) *StackTrace {
frames := make([]Frame, 0, len(pcs))
callerFrames := runtime.CallersFrames(pcs)
for frame, more := callerFrames.Next(); more; frame, more = callerFrames.Next() {
if !strings.HasPrefix(frame.Function, "runtime.") {
frames = append(frames, Frame{frame})
}
}
return &StackTrace{
PCS: pcs,
Frames: frames[:len(frames):len(frames)],
}
}
// FramesStrings returns the frames of this stacktrace as slice of strings.
func (s *StackTrace) FramesStrings() []string {
str := make([]string, len(s.Frames))
for i, frame := range s.Frames {
str[i] = frame.String()
}
return str
}
// Count reports the total number of frames in this stacktrace.
func (s *StackTrace) Count() int {
return len(s.Frames)
}
// Format formats the stack of Frames according to the fmt.Formatter interface.
func (s *StackTrace) Format(fs fmt.State, verb rune) {
for i, frame := range s.Frames {
if i > 0 {
_, _ = io.WriteString(fs, "\n")
}
frame.Format(fs, verb)
}
}
// String returns a string representation of the stack trace.
func (s *StackTrace) String() string {
var sb strings.Builder
for i, frame := range s.Frames {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(fmt.Sprintf("[%d] ", len(s.Frames)-i))
sb.WriteString(frame.String())
}
return sb.String()
}