OLD | NEW |
| (Empty) |
1 package data | |
2 | |
3 import ( | |
4 "bufio" | |
5 "bytes" | |
6 "fmt" | |
7 "regexp" | |
8 "strconv" | |
9 "strings" | |
10 | |
11 "github.com/skia-dev/glog" | |
12 "go.skia.org/infra/fuzzer/go/common" | |
13 ) | |
14 | |
15 type StackTrace struct { | |
16 Frames []StackTraceFrame `json:"frames"` | |
17 } | |
18 | |
19 type StackTraceFrame struct { | |
20 PackageName string `json:"packageName"` | |
21 FileName string `json:"fileName"` | |
22 LineNumber int `json:"lineNumber"` | |
23 FunctionName string `json:"functionName"` | |
24 } | |
25 | |
26 // The `?:` at the beginning of the groups prevent them from being captured | |
27 // \1 is the "package", \2 is the file name, \3 is the line number, \4 is the fu
nction symbol string | |
28 var segvStackTraceLine = regexp.MustCompile(`(?:\.\./)+(?P<package>(?:\w+/)+)(?P
<file>.+):(?P<line>\d+).*\(_(?P<symbol>.*)\)`) | |
29 | |
30 // parseCatchsegvStackTrace takes the contents of a dump file of a catchsegv run
, and returns the | |
31 // parsed stacktrace | |
32 func parseCatchsegvStackTrace(contents string) StackTrace { | |
33 r := bytes.NewBufferString(contents) | |
34 scan := bufio.NewScanner(r) | |
35 | |
36 hasBegun := false | |
37 | |
38 frames := make([]StackTraceFrame, 0, 5) | |
39 | |
40 for scan.Scan() { | |
41 line := scan.Text() | |
42 if strings.Contains(line, "Backtrace") { | |
43 hasBegun = true | |
44 } | |
45 if strings.Contains(line, "Memory map") { | |
46 break | |
47 } | |
48 if !hasBegun { | |
49 continue | |
50 } | |
51 | |
52 if match := segvStackTraceLine.FindStringSubmatch(line); match !
= nil { | |
53 // match[0] is the entire matched portion, [1] is the "p
ackage", [2] is the file name, | |
54 // [3] is the line number and [4] is the encoded functio
n symbol string. | |
55 newFrame := FullStackFrame(match[1], match[2], catchsegv
FunctionName(match[4]), safeParseInt(match[3])) | |
56 frames = append(frames, newFrame) | |
57 } | |
58 } | |
59 return StackTrace{Frames: frames} | |
60 } | |
61 | |
62 // safeParseInt parses a string that is known to contain digits into an int. | |
63 // It may fail if the number is larger than MAX_INT, but it is unlikely those | |
64 // numbers would come up in the stacktraces. | |
65 func safeParseInt(n string) int { | |
66 if i, err := strconv.Atoi(n); err != nil { | |
67 glog.Errorf("Could not parse number from known digits %q: %v", n
, err) | |
68 return 0 | |
69 } else { | |
70 return i | |
71 } | |
72 } | |
73 | |
74 var staticStart = regexp.MustCompile(`^ZL(\d+)`) | |
75 var nonstaticStart = regexp.MustCompile(`^Z(\d+)`) | |
76 | |
77 var methodStart = regexp.MustCompile(`^(ZNK?)(\d+)`) | |
78 var methodName = regexp.MustCompile(`^(\d+)`) | |
79 | |
80 // catchsegvFunctionName parses the symbol string from a stacktrace to | |
81 // get the name of the related function. | |
82 //TODO(kjlubick) parse arguments if that helps the readability of the stacktrace
s | |
83 // Here are some examples of symbol strings and what the various chars mean: | |
84 // (Parameters are after the names, but are unparsed as of yet) | |
85 // ZL12convert_to_8 -> ZL12 -> static function 12 long "convert_to_8" | |
86 // Z9tool_mainiPPc -> non-static function, 9 long "tool_main" | |
87 // ZN14SkBmpMaskCodec10decodeRows -> ZN14 -> type is 14 long, method name is 10
long "decodeRows" | |
88 // ZNK2DM6SKPSrc4drawEP8SkCanvas -> ZNK2 -> type is 2 long (method is konstant)
"DM" -> | |
89 // 6 -> Sub type is 6 long "SKPSrc" -> 4 -> method is 4 long "draw" | |
90 // (since there is no number directly after the 3rd step, we have found the p
aram boundary). | |
91 func catchsegvFunctionName(s string) string { | |
92 if match := methodStart.FindStringSubmatch(s); match != nil { | |
93 length := safeParseInt(match[2]) | |
94 // ZNK? is 2-3 chars, so slice (num letters + num digits + numbe
r of spaces) chars off | |
95 // the beginning. | |
96 s = s[len(match[1])+len(match[2])+length:] | |
97 f := "" | |
98 // We look at the beginning of our trimmed string for numbers. | |
99 // if there are numbers, we pull off a piece of the name and sca
n again. | |
100 // If there is more than one piece, we separate it with ::, beca
use it is a nested type | |
101 // or enum thing. | |
102 for match := methodName.FindStringSubmatch(s); match != nil; mat
ch = methodName.FindStringSubmatch(s) { | |
103 if f != "" { | |
104 f += "::" | |
105 } | |
106 length = safeParseInt(match[1]) | |
107 start := len(match[1]) | |
108 f += s[start : start+length] | |
109 s = s[start+length:] | |
110 } | |
111 return f | |
112 } | |
113 if match := staticStart.FindStringSubmatch(s); match != nil { | |
114 length := safeParseInt(match[1]) | |
115 // ZL is 2 chars, so advance 2 spaces + how many digits there ar
e | |
116 start := 2 + len(match[1]) | |
117 return s[start : start+length] | |
118 } | |
119 if match := nonstaticStart.FindStringSubmatch(s); match != nil { | |
120 length := safeParseInt(match[1]) | |
121 // Z is 1 char, so advance 1 space + how many digits there are | |
122 start := 1 + len(match[1]) | |
123 return s[start : start+length] | |
124 } | |
125 return common.UNKNOWN_FUNCTION | |
126 } | |
127 | |
128 // The `?:` at the beginning of the groups prevent them from being captured | |
129 // \1 is the (hopefully symbolized) function name, \2 is the "package", \3 is th
e file name, | |
130 // \4 is the line number | |
131 var asanStackTraceLine = regexp.MustCompile(`in (?P<function>[a-zA-Z0-9_:]+).*(?
:\.\./)+(?P<package>(?:\w+/)+)(?P<file>.+?):(?P<line>\d+)`) | |
132 | |
133 // parseCatchsegvStackTrace takes the output of an AddressSanitizer crash, and r
eturns the parsed | |
134 // StackTrace, if it is able to find one. If the result is not symbolized, this
will return | |
135 // an empty StackTrace. | |
136 func parseASANStackTrace(contents string) StackTrace { | |
137 r := bytes.NewBufferString(contents) | |
138 scan := bufio.NewScanner(r) | |
139 | |
140 hasBegun := false | |
141 | |
142 frames := make([]StackTraceFrame, 0, 5) | |
143 | |
144 for scan.Scan() { | |
145 line := scan.Text() | |
146 if strings.Contains(line, "ERROR: AddressSanitizer:") { | |
147 hasBegun = true | |
148 continue | |
149 } | |
150 if hasBegun && line == "" { | |
151 break | |
152 } | |
153 if !hasBegun { | |
154 continue | |
155 } | |
156 | |
157 line = strings.Replace(line, "(anonymous namespace)::", "", -1) | |
158 | |
159 if match := asanStackTraceLine.FindStringSubmatch(line); match !
= nil { | |
160 // match[0] is the entire matched portion, [1] is the fu
nction name [2] is the | |
161 // "package", [3] is the file name, [4] is the line numb
er | |
162 newFrame := FullStackFrame(match[2], match[3], match[1],
safeParseInt(match[4])) | |
163 frames = append(frames, newFrame) | |
164 } | |
165 } | |
166 return StackTrace{Frames: frames} | |
167 } | |
168 | |
169 // FullStackFrame creates a StackTraceFrame with all components | |
170 func FullStackFrame(packageName, fileName, functionName string, lineNumber int)
StackTraceFrame { | |
171 return StackTraceFrame{ | |
172 PackageName: packageName, | |
173 FileName: fileName, | |
174 LineNumber: lineNumber, | |
175 FunctionName: functionName, | |
176 } | |
177 } | |
178 | |
179 func (f *StackTraceFrame) String() string { | |
180 return fmt.Sprintf("%s%s:%d %s", f.PackageName, f.FileName, f.LineNumber
, f.FunctionName) | |
181 } | |
182 | |
183 func (st *StackTrace) String() string { | |
184 s := fmt.Sprintf("StackTrace with %d frames:\n", len(st.Frames)) | |
185 for _, f := range st.Frames { | |
186 s += fmt.Sprintf("\t%s\n", f.String()) | |
187 } | |
188 return s | |
189 } | |
190 | |
191 func (st *StackTrace) IsEmpty() bool { | |
192 return len(st.Frames) == 0 | |
193 } | |
OLD | NEW |