debugstack

package
v0.0.0-...-6b39aad Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2025 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package debugstack has functions for capturing a debugging stack (like `debug.Stack()` but with a bit more configurability (able to skip frames and filter frames from unwanted packages).

Why not use e.g. `gostackparse`? This is a really good question, but there are some differences between debugstack and gostackparse:

  1. debugstack aims to always re-create the original stacktrace text verbatim (assuming the stack is not filtered), even in the presence of new, unknown, stack frame types. gostackparse drops data (like frame pointer offsets), and does not explicitly capture interleaved frame variants (like haivng elided frames in the middle of other stack frames).
  2. debugstack has stack filtering functionality.
  3. debugstack is explicitly aimed at parsing the output of debug.Stack(), not of parsing the multi-stack snapshot that gostackparse is aimed at.
  4. debugstack only does the minimal amount of parsing necessary to return a filterable stack, whereas gostackparse is trying to extract all semantic information, including goroutine IDs, etc.

This package is not as optimized as gostackparse (as of writing it operates at about 50MB/s vs gostackparse's 500MB/s on a macbook m3 pro), though this package is a bit more memory efficient (~15% fewer alloc's op).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Capture

func Capture(skip int) string

Capture returns a debug.Stack() output after dropping `skip` number of additional frames.

A skip value of 0 means that the caller of Capture will be the top frame.

Types

type CompiledRule

type CompiledRule struct {
	// contains filtered or unexported fields
}

func CompileRules

func CompileRules(rules ...Rule) CompiledRule

type Frame

type Frame struct {
	// Kind is the detected type-of-frame.
	Kind FrameKind

	// PkgName is the package name, if this Frame has one.
	//
	// This will be something like `name/of/pkg` or `stdlibpkg`.
	//
	// NOTE: This is obtained by parsing the fully-qualified function name from
	// the first line of relevant frames until the last '/', and then scanning
	// until the first '.'. This means that if a package has a '.' in the last
	// directory there will be an incorrect parse.
	//
	// My best advice is "don't do that", but I'm documenting it here because
	// I assume it will trip someone (possibly me) up, and hopefully this doctring
	// saves them a couple minutes.
	//
	// This will be a strict substring of one of the strings in Lines.
	PkgName string

	// FuncName is the unqualified function name which is the remainder of the
	// line after parsing PkgName.
	//
	// See the NOTE on PkgName - the reciprocal issue could occur in FuncName
	// given the criteria documented in PkgName.
	//
	// This will be a strict substring of one of the strings in Lines.
	FuncName string

	// Lines are the original lines (including newlines) which corresponded with
	// this Frame.
	Lines []string
}

Frame represents a parsed stack frame in a Trace.

This contains the original lines that were parsed (as .Lines), as well as PkgName and/or FuncName (if the frame contained them).

If you just want to reconstruct the original stack trace, you just need to concatenate Lines and can ignore the other fields entirely.

func (*Frame) SourceInfo

func (f *Frame) SourceInfo() (path string, lineNo int)

SourceInfo returns the source path and line number (if this frame contains them).

If this Frame does not have path info, this returns ("", -1) If this Frame has a path, but does not have lineNo info, this returns (path, 0)

This function does not validate that `path` is anything resembling a real path, in the case that you Parse'd some garbled junk.

UnknownFrameKind Frames never have SourceInfo.

type FrameKind

type FrameKind int
const (
	// UnknownFrameKind is used for lines which don't parse
	// into any of the other kinds.
	//
	// These will always contain a single line.
	UnknownFrameKind FrameKind = 1 << iota

	// goroutine NN [<goroutine status>]:
	GoroutineHeaderKind

	// [originating from goroutine NN]:
	GoroutineAncestryKind

	// ...(additional| NN) frames elided...
	FramesElidedKind

	// created by path/of/pkg.funcName [in goroutine NN]
	// <tab>/path/to/file.go:NN[ +0xNN]
	CreatedByFrameKind

	// non-Go function at pc=...
	// <tab>/path/to/file.go:NN[ +0xNN]
	CgoUnknownFrameKind

	// path/of/pkg.funcName(...)
	// <tab>/path/to/file.go:NN[ +0xNN]
	StackFrameKind
)

func (FrameKind) MaskAffected

func (f FrameKind) MaskAffected() iter.Seq[FrameKind]

MaskAffected yields all FrameKind values indicated by `f` as a bit mask.

This will always yield FrameKind values from low to high.

func (FrameKind) String

func (i FrameKind) String() string

type Rule

type Rule struct {
	// ApplyTo is a bit mask of the FrameKinds which this Rule applies to.
	ApplyTo FrameKind

	// Drop indicates that you want all frame types in ApplyTo to be removed.
	Drop bool

	// DropN indicates how many of these frames to drop, starting from the top.
	//
	// This N applies independently to each FrameKind indicated by ApplyTo (e.g.
	// 7 with ApplyTo=StackFrameKind|CreatedByFrameKind means that each of
	// StackFrameKind and CreatedByFrameKind will drop up to 7 frames).
	DropN int

	// DropIfInPkg is a list of package regexps to match the package name, for
	// frames which have PkgName.
	DropIfInPkg []*regexp.Regexp
}

Rule is a single rule which indicates which types of frames it applies to, and has multiple ways to drop frames of this kind. If multiple drop rules are set, they apply in order (i.e. Drop, then DropN, then DropIfInPkg).

type Trace

type Trace []Frame

Trace is a linear collection of Frames.

You can retrieve the identical bytes passed to Parse by concatenating the Lines in the Frames contained here (which is what Trace.String does).

Traces can also be filtered with Trace.Filter to remove frames for certain packages, remove some frames from the top, etc.

func Parse

func Parse(trace []byte) Trace

Parse expects a single stack trace from e.g. debug.Stack(), and will return the parsed Trace (as best as can be determined).

Passing garbage in here will just get you a Trace with UnknownFrameKind frames, one per line.

The original data `trace` can be obtained by calling Trace.String.

func ParseString

func ParseString(trace string) Trace

ParseString is just the string variant of Parse, in case you are consuming a `string` stack trace..

func (Trace) Filter

func (t Trace) Filter(rule CompiledRule, elide bool) Trace

Filter returns a new Trace which contains only Frames which match the CompiledRule.

If `elide` is true, this will insert a FramesElidedKind frame wherever one or more frames were removed.

func (Trace) Stats

func (t Trace) Stats() map[FrameKind]int

Stats returns a mapping of FrameKind to the number of times that kind of frame appears in the Trace.

func (Trace) String

func (t Trace) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL