| // 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) | |
| } |