...
1
2
3
4
5 package stack
6
7 import (
8 "bufio"
9 "errors"
10 "io"
11 "regexp"
12 "strconv"
13 )
14
15 var (
16 reBlank = regexp.MustCompile(`^\s*$`)
17 reGoroutine = regexp.MustCompile(`^\s*goroutine (\d+) \[([^\]]*)\]:\s*$`)
18 reCall = regexp.MustCompile(`^\s*` +
19 `(created by )?` +
20 `(([\w/.]+/)?[\w]+)\.` +
21 `(\(([^:.)]*)\)\.)?` +
22 `([\w\.]+)` +
23 `(\(.*\))?` +
24 `\s*$`)
25 rePos = regexp.MustCompile(`^\s*(.*):(\d+)( .*)?$`)
26
27 errBreakParse = errors.New("break parse")
28 )
29
30
31
32 type Scanner struct {
33 lines *bufio.Scanner
34 done bool
35 }
36
37
38 func NewScanner(r io.Reader) *Scanner {
39 s := &Scanner{
40 lines: bufio.NewScanner(r),
41 }
42 s.Skip()
43 return s
44 }
45
46
47 func (s *Scanner) Peek() string {
48 if s.done {
49 return ""
50 }
51 return s.lines.Text()
52 }
53
54
55
56 func (s *Scanner) Skip() {
57 if !s.lines.Scan() {
58 s.done = true
59 }
60 }
61
62
63 func (s *Scanner) Next() string {
64 line := s.Peek()
65 s.Skip()
66 return line
67 }
68
69
70
71 func (s *Scanner) Done() bool {
72 return s.done
73 }
74
75
76
77 func (s *Scanner) Err() error {
78 return s.lines.Err()
79 }
80
81
82
83 func (s *Scanner) Match(re *regexp.Regexp) []string {
84 if s.done {
85 return nil
86 }
87 match := re.FindStringSubmatch(s.Peek())
88 if match != nil {
89 s.Skip()
90 }
91 return match
92 }
93
94
95 func (s *Scanner) SkipBlank() {
96 for !s.done {
97 line := s.Peek()
98 if len(line) != 0 && !reBlank.MatchString(line) {
99 return
100 }
101 s.Skip()
102 }
103 }
104
105
106
107 func Parse(scanner *Scanner) (Dump, error) {
108 dump := Dump{}
109 for {
110 gr, ok := parseGoroutine(scanner)
111 if !ok {
112 return dump, nil
113 }
114 dump = append(dump, gr)
115 }
116 }
117
118 func parseGoroutine(scanner *Scanner) (Goroutine, bool) {
119 match := scanner.Match(reGoroutine)
120 if match == nil {
121 return Goroutine{}, false
122 }
123 id, _ := strconv.ParseInt(match[1], 0, 32)
124 gr := Goroutine{
125 ID: int(id),
126 State: match[2],
127 }
128 for {
129 frame, ok := parseFrame(scanner)
130 if !ok {
131 scanner.SkipBlank()
132 return gr, true
133 }
134 if frame.Position.Filename != "" {
135 gr.Stack = append(gr.Stack, frame)
136 }
137 }
138 }
139
140 func parseFrame(scanner *Scanner) (Frame, bool) {
141 fun, ok := parseFunction(scanner)
142 if !ok {
143 return Frame{}, false
144 }
145 frame := Frame{
146 Function: fun,
147 }
148 frame.Position, ok = parsePosition(scanner)
149
150
151
152
153 return frame, ok
154 }
155
156 func parseFunction(scanner *Scanner) (Function, bool) {
157 match := scanner.Match(reCall)
158 if match == nil {
159 return Function{}, false
160 }
161 return Function{
162 Package: match[2],
163 Type: match[5],
164 Name: match[6],
165 }, true
166 }
167
168 func parsePosition(scanner *Scanner) (Position, bool) {
169 match := scanner.Match(rePos)
170 if match == nil {
171 return Position{}, false
172 }
173 line, _ := strconv.ParseInt(match[2], 0, 32)
174 return Position{Filename: match[1], Line: int(line)}, true
175 }
176
View as plain text