cairn/internal/fingerprint/parser_asan.go

69 lines
1.5 KiB
Go

package fingerprint
import (
"regexp"
"strconv"
"strings"
)
// ASan/MSan/TSan/UBSan frame patterns:
//
// #0 0x55a3b4 in function_name /path/to/file.c:42:13
// #0 0x55a3b4 in function_name (/path/to/binary+0x1234)
// #1 0x55a3b4 (/path/to/binary+0x1234)
var asanFrameRe = regexp.MustCompile(
`^\s*#(\d+)\s+(0x[0-9a-fA-F]+)\s+(?:in\s+(\S+)\s+)?(.*)$`,
)
// ASan error header line, e.g.:
//
// ==12345==ERROR: AddressSanitizer: heap-buffer-overflow
var asanHeaderRe = regexp.MustCompile(
`==\d+==ERROR:\s+(Address|Memory|Thread|Undefined)Sanitizer`,
)
// ParseASan parses AddressSanitizer, MemorySanitizer, ThreadSanitizer,
// and UndefinedBehaviorSanitizer stack traces.
func ParseASan(raw string) []Frame {
if !asanHeaderRe.MatchString(raw) {
return nil
}
var frames []Frame
for _, line := range strings.Split(raw, "\n") {
m := asanFrameRe.FindStringSubmatch(line)
if m == nil {
continue
}
idx, _ := strconv.Atoi(m[1])
addr := m[2]
fn := m[3]
location := m[4]
var file string
var lineNo int
// Try to extract file:line from location.
if parts := strings.SplitN(location, ":", 3); len(parts) >= 2 {
// Could be /path/to/file.c:42 or (/binary+0x1234)
if !strings.HasPrefix(parts[0], "(") {
file = parts[0]
if len(parts) >= 2 {
lineNo, _ = strconv.Atoi(parts[1])
}
}
}
frames = append(frames, Frame{
Index: idx,
Address: addr,
Function: fn,
File: strings.TrimSpace(file),
Line: lineNo,
})
}
return frames
}