blob: 479301a78d51cf9a954fbeb1143fdfd883860ba0 [file] [log] [blame]
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package stack provides support for parsing standard goroutine stack traces.
package stack
import (
"fmt"
"text/tabwriter"
)
// Dump is a raw set of goroutines and their stacks.
type Dump []Goroutine
// Goroutine is a single parsed goroutine dump.
type Goroutine struct {
State string // state that the goroutine is in.
ID int // id of the goroutine.
Stack Stack // call frames that make up the stack
}
// Stack is a set of frames in a callstack.
type Stack []Frame
// Frame is a point in a call stack.
type Frame struct {
Function Function
Position Position
}
// Function is the function called at a frame.
type Function struct {
Package string // package name of function if known
Type string // if set function is a method of this type
Name string // function name of the frame
}
// Position is the file position for a frame.
type Position struct {
Filename string // source filename
Line int // line number within file
}
// Summary is a set of stacks processed and collated into Calls.
type Summary struct {
Total int // the total count of goroutines in the summary
Calls []Call // the collated stack traces
}
// Call is set of goroutines that all share the same callstack.
// They will be grouped by state.
type Call struct {
Stack Stack // the shared callstack information
Groups []Group // the sets of goroutines with the same state
}
// Group is a set of goroutines with the same stack that are in the same state.
type Group struct {
State string // the shared state of the goroutines
Goroutines []Goroutine // the set of goroutines in this group
}
// Delta represents the difference between two stack dumps.
type Delta struct {
Before Dump // The goroutines that were only in the before set.
Shared Dump // The goroutines that were in both sets.
After Dump // The goroutines that were only in the after set.
}
func (s Stack) equal(other Stack) bool {
if len(s) != len(other) {
return false
}
for i, frame := range s {
if !frame.equal(other[i]) {
return false
}
}
return true
}
func (s Stack) less(other Stack) bool {
for i, frame := range s {
if i >= len(other) {
return false
}
if frame.less(other[i]) {
return true
}
if !frame.equal(other[i]) {
return false
}
}
return len(s) < len(other)
}
func (f Frame) equal(other Frame) bool {
return f.Position.equal(other.Position)
}
func (f Frame) less(other Frame) bool {
return f.Position.less(other.Position)
}
func (p Position) equal(other Position) bool {
return p.Filename == other.Filename && p.Line == other.Line
}
func (p Position) less(other Position) bool {
if p.Filename < other.Filename {
return true
}
if p.Filename > other.Filename {
return false
}
return p.Line < other.Line
}
func (s Summary) Format(w fmt.State, r rune) {
tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
for i, c := range s.Calls {
if i > 0 {
fmt.Fprintf(tw, "\n\n")
tw.Flush()
}
fmt.Fprint(tw, c)
}
tw.Flush()
if s.Total > 0 && w.Flag('+') {
fmt.Fprintf(w, "\n\n%d goroutines, %d unique", s.Total, len(s.Calls))
}
}
func (c Call) Format(w fmt.State, r rune) {
for i, g := range c.Groups {
if i > 0 {
fmt.Fprint(w, " ")
}
fmt.Fprint(w, g)
}
for _, f := range c.Stack {
fmt.Fprintf(w, "\n%v", f)
}
}
func (g Group) Format(w fmt.State, r rune) {
fmt.Fprintf(w, "[%v]: ", g.State)
for i, gr := range g.Goroutines {
if i > 0 {
fmt.Fprint(w, ", ")
}
fmt.Fprintf(w, "$%d", gr.ID)
}
}
func (f Frame) Format(w fmt.State, c rune) {
fmt.Fprintf(w, "%v:\t%v", f.Position, f.Function)
}
func (f Function) Format(w fmt.State, c rune) {
if f.Type != "" {
fmt.Fprintf(w, "(%v).", f.Type)
}
fmt.Fprintf(w, "%v", f.Name)
}
func (p Position) Format(w fmt.State, c rune) {
fmt.Fprintf(w, "%v:%v", p.Filename, p.Line)
}