| 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 |