mirror of
https://github.com/golang/go
synced 2024-11-05 14:46:11 -07:00
a02cf32866
This can be used either to directly parse runtime.Stack output or process text that includes stack dumps, like test timeouts or panics. It includes a binary, gostacks that processes stdin to stdout replacing stack dumps in place. Change-Id: Id7b1cfd69b8aea36c66f12ec0bdf38b68cba5afb Reviewed-on: https://go-review.googlesource.com/c/tools/+/232658 Run-TryBot: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
171 lines
3.9 KiB
Go
171 lines
3.9 KiB
Go
// 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)
|
|
}
|