panicparse: github.com/maruel/panicparse/stack Index | Examples | Files

package stack

import "github.com/maruel/panicparse/stack"

Package stack analyzes stack dump of Go processes and simplifies it.

It is mostly useful on servers will large number of identical goroutines, making the crash dump harder to read than strictly necessary.

Code:

package main

import (
    "bytes"
    "fmt"
    "io"
    "os"

    "github.com/maruel/panicparse/stack"
)

const crash = `panic: oh no!

goroutine 1 [running]:
panic(0x0, 0x0)
	/home/user/src/golang/src/runtime/panic.go:464 +0x3e6
main.crash2(0x7fe50b49d028, 0xc82000a1e0)
	/home/user/go/src/github.com/maruel/panicparse/cmd/pp/main.go:45 +0x23
main.main()
	/home/user/go/src/github.com/maruel/panicparse/cmd/pp/main.go:50 +0xa6
`

func main() {
    // Optional: Check for GOTRACEBACK being set, in particular if there is only
    // one goroutine returned.
    in := bytes.NewBufferString(crash)
    c, err := stack.ParseDump(in, os.Stdout, true)
    if err != nil {
        return
    }

    // Find out similar goroutine traces and group them into buckets.
    buckets := stack.Aggregate(c.Goroutines, stack.AnyValue)

    // Calculate alignment.
    srcLen := 0
    pkgLen := 0
    for _, bucket := range buckets {
        for _, line := range bucket.Signature.Stack.Calls {
            if l := len(line.SrcLine()); l > srcLen {
                srcLen = l
            }
            if l := len(line.Func.PkgName()); l > pkgLen {
                pkgLen = l
            }
        }
    }

    for _, bucket := range buckets {
        // Print the goroutine header.
        extra := ""
        if s := bucket.SleepString(); s != "" {
            extra += " [" + s + "]"
        }
        if bucket.Locked {
            extra += " [locked]"
        }
        if c := bucket.CreatedByString(false); c != "" {
            extra += " [Created by " + c + "]"
        }
        fmt.Printf("%d: %s%s\n", len(bucket.IDs), bucket.State, extra)

        // Print the stack lines.
        for _, line := range bucket.Stack.Calls {
            fmt.Printf(
                "    %-*s %-*s %s(%s)\n",
                pkgLen, line.Func.PkgName(), srcLen, line.SrcLine(),
                line.Func.Name(), &line.Args)
        }
        if bucket.Stack.Elided {
            io.WriteString(os.Stdout, "    (...)\n")
        }
    }
}

Index

Examples

Package Files

bucket.go context.go source.go stack.go

func Augment Uses

func Augment(goroutines []*Goroutine)

Augment processes source files to improve calls to be more descriptive.

It modifies goroutines in place. It requires calling ParseDump() with guesspaths set to true to work properly.

type Arg Uses

type Arg struct {
    Value uint64 // Value is the raw value as found in the stack trace
    Name  string // Name is a pseudo name given to the argument
}

Arg is an argument on a Call.

func (*Arg) IsPtr Uses

func (a *Arg) IsPtr() bool

IsPtr returns true if we guess it's a pointer. It's only a guess, it can be easily be confused by a bitmask.

func (*Arg) String Uses

func (a *Arg) String() string

type Args Uses

type Args struct {
    Values    []Arg    // Values is the arguments as shown on the stack trace. They are mangled via simplification.
    Processed []string // Processed is the arguments generated from processing the source files. It can have a length lower than Values.
    Elided    bool     // If set, it means there was a trailing ", ..."
}

Args is a series of function call arguments.

func (*Args) String Uses

func (a *Args) String() string

type Bucket Uses

type Bucket struct {
    Signature
    // IDs is the ID of each Goroutine with this Signature.
    IDs []int
    // First is true if this Bucket contains the first goroutine, e.g. the one
    // Signature that likely generated the panic() call, if any.
    First bool
}

Bucket is a stack trace signature and the list of goroutines that fits this signature.

func Aggregate Uses

func Aggregate(goroutines []*Goroutine, similar Similarity) []*Bucket

Aggregate merges similar goroutines into buckets.

The buckets are ordered in library provided order of relevancy. You can reorder at your chosing.

type Call Uses

type Call struct {
    SrcPath      string // Full path name of the source file as seen in the trace
    LocalSrcPath string // Full path name of the source file as seen in the host.
    Line         int    // Line number
    Func         Func   // Fully qualified function name (encoded).
    Args         Args   // Call arguments
    IsStdlib     bool   // true if it is a Go standard library function. This includes the 'go test' generated main executable.
}

Call is an item in the stack trace.

func (*Call) FullSrcLine Uses

func (c *Call) FullSrcLine() string

FullSrcLine returns "/path/to/source.go:line".

This file path is mutated to look like the local path.

func (*Call) IsPkgMain Uses

func (c *Call) IsPkgMain() bool

IsPkgMain returns true if it is in the main package.

func (*Call) PkgSrc Uses

func (c *Call) PkgSrc() string

PkgSrc is one directory plus the file name of the source file.

func (*Call) SrcLine Uses

func (c *Call) SrcLine() string

SrcLine returns "source.go:line", including only the base file name.

func (*Call) SrcName Uses

func (c *Call) SrcName() string

SrcName returns the base file name of the source file.

type Context Uses

type Context struct {
    // Goroutines is the Goroutines found.
    //
    // They are in the order that they were printed.
    Goroutines []*Goroutine

    // GOROOT is the GOROOT as detected in the traceback, not the on the host.
    //
    // It can be empty if no root was determined, for example the traceback
    // contains only non-stdlib source references.
    //
    // Empty is guesspaths was false.
    GOROOT string
    // GOPATHs is the GOPATH as detected in the traceback, with the value being
    // the corresponding path mapped to the host.
    //
    // It can be empty if only stdlib code is in the traceback or if no local
    // sources were matched up. In the general case there is only one entry in
    // the map.
    //
    // Nil is guesspaths was false.
    GOPATHs map[string]string
    // contains filtered or unexported fields
}

Context is a parsing context.

It contains the deduced GOROOT and GOPATH, if guesspaths is true.

func ParseDump Uses

func ParseDump(r io.Reader, out io.Writer, guesspaths bool) (*Context, error)

ParseDump processes the output from runtime.Stack().

Returns nil *Context if no stack trace was detected.

It pipes anything not detected as a panic stack trace from r into out. It assumes there is junk before the actual stack trace. The junk is streamed to out.

If guesspaths is false, no guessing of GOROOT and GOPATH is done, and Call entites do not have LocalSrcPath and IsStdlib filled in.

type Func Uses

type Func struct {
    Raw string
}

Func is a function call.

Go stack traces print a mangled function call, this wrapper unmangle the string before printing and adds other filtering methods.

func (*Func) IsExported Uses

func (f *Func) IsExported() bool

IsExported returns true if the function is exported.

func (*Func) Name Uses

func (f *Func) Name() string

Name is the naked function name.

func (*Func) PkgDotName Uses

func (f *Func) PkgDotName() string

PkgDotName returns "<package>.<func>" format.

func (*Func) PkgName Uses

func (f *Func) PkgName() string

PkgName is the package name for this function reference.

func (*Func) String Uses

func (f *Func) String() string

String is the fully qualified function name.

Sadly Go is a bit confused when the package name doesn't match the directory containing the source file and will use the directory name instead of the real package name.

type Goroutine Uses

type Goroutine struct {
    Signature      // It's stack trace, internal bits, state, which call site created it, etc.
    ID        int  // Goroutine ID.
    First     bool // First is the goroutine first printed, normally the one that crashed.
}

Goroutine represents the state of one goroutine, including the stack trace.

type Signature Uses

type Signature struct {
    // Use git grep 'gopark(|unlock)\(' to find them all plus everything listed
    // in runtime/traceback.go. Valid values includes:
    //     - chan send, chan receive, select
    //     - finalizer wait, mark wait (idle),
    //     - Concurrent GC wait, GC sweep wait, force gc (idle)
    //     - IO wait, panicwait
    //     - semacquire, semarelease
    //     - sleep, timer goroutine (idle)
    //     - trace reader (blocked)
    // Stuck cases:
    //     - chan send (nil chan), chan receive (nil chan), select (no cases)
    // Runnable states:
    //    - idle, runnable, running, syscall, waiting, dead, enqueue, copystack,
    // Scan states:
    //    - scan, scanrunnable, scanrunning, scansyscall, scanwaiting, scandead,
    //      scanenqueue
    State     string
    CreatedBy Call // Which other goroutine which created this one.
    SleepMin  int  // Wait time in minutes, if applicable.
    SleepMax  int  // Wait time in minutes, if applicable.
    Stack     Stack
    Locked    bool // Locked to an OS thread.
}

Signature represents the signature of one or multiple goroutines.

It is effectively the stack trace plus the goroutine internal bits, like it's state, if it is thread locked, which call site created this goroutine, etc.

func (*Signature) CreatedByString Uses

func (s *Signature) CreatedByString(fullPath bool) string

CreatedByString return a short context about the origin of this goroutine signature.

func (*Signature) SleepString Uses

func (s *Signature) SleepString() string

SleepString returns a string "N-M minutes" if the goroutine(s) slept for a long time.

Returns an empty string otherwise.

type Similarity Uses

type Similarity int

Similarity is the level at which two call lines arguments must match to be considered similar enough to coalesce them.

const (
    // ExactFlags requires same bits (e.g. Locked).
    ExactFlags Similarity = iota
    // ExactLines requests the exact same arguments on the call line.
    ExactLines
    // AnyPointer considers different pointers a similar call line.
    AnyPointer
    // AnyValue accepts any value as similar call line.
    AnyValue
)

type Stack Uses

type Stack struct {
    Calls  []Call // Call stack. First is original function, last is leaf function.
    Elided bool   // Happens when there's >100 items in Stack, currently hardcoded in package runtime.
}

Stack is a call stack.

Package stack imports 22 packages (graph) and is imported by 33 packages. Updated 2019-07-20. Refresh now. Tools for package owners.