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 }