1 | // Copyright 2020 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | // Package stack provides support for parsing standard goroutine stack traces. |
6 | package stack |
7 | |
8 | import ( |
9 | "fmt" |
10 | "text/tabwriter" |
11 | ) |
12 | |
13 | // Dump is a raw set of goroutines and their stacks. |
14 | type Dump []Goroutine |
15 | |
16 | // Goroutine is a single parsed goroutine dump. |
17 | type Goroutine struct { |
18 | State string // state that the goroutine is in. |
19 | ID int // id of the goroutine. |
20 | Stack Stack // call frames that make up the stack |
21 | } |
22 | |
23 | // Stack is a set of frames in a callstack. |
24 | type Stack []Frame |
25 | |
26 | // Frame is a point in a call stack. |
27 | type Frame struct { |
28 | Function Function |
29 | Position Position |
30 | } |
31 | |
32 | // Function is the function called at a frame. |
33 | type Function struct { |
34 | Package string // package name of function if known |
35 | Type string // if set function is a method of this type |
36 | Name string // function name of the frame |
37 | } |
38 | |
39 | // Position is the file position for a frame. |
40 | type Position struct { |
41 | Filename string // source filename |
42 | Line int // line number within file |
43 | } |
44 | |
45 | // Summary is a set of stacks processed and collated into Calls. |
46 | type Summary struct { |
47 | Total int // the total count of goroutines in the summary |
48 | Calls []Call // the collated stack traces |
49 | } |
50 | |
51 | // Call is set of goroutines that all share the same callstack. |
52 | // They will be grouped by state. |
53 | type Call struct { |
54 | Stack Stack // the shared callstack information |
55 | Groups []Group // the sets of goroutines with the same state |
56 | } |
57 | |
58 | // Group is a set of goroutines with the same stack that are in the same state. |
59 | type Group struct { |
60 | State string // the shared state of the goroutines |
61 | Goroutines []Goroutine // the set of goroutines in this group |
62 | } |
63 | |
64 | // Delta represents the difference between two stack dumps. |
65 | type Delta struct { |
66 | Before Dump // The goroutines that were only in the before set. |
67 | Shared Dump // The goroutines that were in both sets. |
68 | After Dump // The goroutines that were only in the after set. |
69 | } |
70 | |
71 | func (s Stack) equal(other Stack) bool { |
72 | if len(s) != len(other) { |
73 | return false |
74 | } |
75 | for i, frame := range s { |
76 | if !frame.equal(other[i]) { |
77 | return false |
78 | } |
79 | } |
80 | return true |
81 | } |
82 | |
83 | func (s Stack) less(other Stack) bool { |
84 | for i, frame := range s { |
85 | if i >= len(other) { |
86 | return false |
87 | } |
88 | if frame.less(other[i]) { |
89 | return true |
90 | } |
91 | if !frame.equal(other[i]) { |
92 | return false |
93 | } |
94 | } |
95 | return len(s) < len(other) |
96 | } |
97 | |
98 | func (f Frame) equal(other Frame) bool { |
99 | return f.Position.equal(other.Position) |
100 | } |
101 | |
102 | func (f Frame) less(other Frame) bool { |
103 | return f.Position.less(other.Position) |
104 | } |
105 | |
106 | func (p Position) equal(other Position) bool { |
107 | return p.Filename == other.Filename && p.Line == other.Line |
108 | } |
109 | |
110 | func (p Position) less(other Position) bool { |
111 | if p.Filename < other.Filename { |
112 | return true |
113 | } |
114 | if p.Filename > other.Filename { |
115 | return false |
116 | } |
117 | return p.Line < other.Line |
118 | } |
119 | |
120 | func (s Summary) Format(w fmt.State, r rune) { |
121 | tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0) |
122 | for i, c := range s.Calls { |
123 | if i > 0 { |
124 | fmt.Fprintf(tw, "\n\n") |
125 | tw.Flush() |
126 | } |
127 | fmt.Fprint(tw, c) |
128 | } |
129 | tw.Flush() |
130 | if s.Total > 0 && w.Flag('+') { |
131 | fmt.Fprintf(w, "\n\n%d goroutines, %d unique", s.Total, len(s.Calls)) |
132 | } |
133 | } |
134 | |
135 | func (c Call) Format(w fmt.State, r rune) { |
136 | for i, g := range c.Groups { |
137 | if i > 0 { |
138 | fmt.Fprint(w, " ") |
139 | } |
140 | fmt.Fprint(w, g) |
141 | } |
142 | for _, f := range c.Stack { |
143 | fmt.Fprintf(w, "\n%v", f) |
144 | } |
145 | } |
146 | |
147 | func (g Group) Format(w fmt.State, r rune) { |
148 | fmt.Fprintf(w, "[%v]: ", g.State) |
149 | for i, gr := range g.Goroutines { |
150 | if i > 0 { |
151 | fmt.Fprint(w, ", ") |
152 | } |
153 | fmt.Fprintf(w, "$%d", gr.ID) |
154 | } |
155 | } |
156 | |
157 | func (f Frame) Format(w fmt.State, c rune) { |
158 | fmt.Fprintf(w, "%v:\t%v", f.Position, f.Function) |
159 | } |
160 | |
161 | func (f Function) Format(w fmt.State, c rune) { |
162 | if f.Type != "" { |
163 | fmt.Fprintf(w, "(%v).", f.Type) |
164 | } |
165 | fmt.Fprintf(w, "%v", f.Name) |
166 | } |
167 | |
168 | func (p Position) Format(w fmt.State, c rune) { |
169 | fmt.Fprintf(w, "%v:%v", p.Filename, p.Line) |
170 | } |
171 |
Members