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