package fingerprint import ( "path/filepath" "regexp" "strings" ) const maxFrames = 8 var ( hexAddrRe = regexp.MustCompile(`0x[0-9a-fA-F]+`) templateParamRe = regexp.MustCompile(`<[^>]*>`) abiTagRe = regexp.MustCompile(`\[abi:[^\]]*\]`) ) // runtimePrefixes are function prefixes for runtime/library frames to filter out. var runtimePrefixes = []string{ "__libc_", "__GI_", "_start", "__clone", "start_thread", "__pthread_", "__sigaction", "_dl_", "__tls_", // glibc allocator internals "__libc_malloc", "__libc_free", "malloc", "free", "realloc", "calloc", // ASan runtime "__asan_", "__sanitizer_", "__interceptor_", "__interception::", // Zig std runtime "std.debug.", "std.start.", "std.os.linux.", "posixCallNative", } // Normalize applies stability-oriented transformations to parsed frames. func Normalize(frames []Frame) []NormalizedFrame { var result []NormalizedFrame for _, f := range frames { // Skip inline frames. if f.Inline { continue } fn := f.Function // Skip runtime/library frames. if isRuntimeFrame(fn) { continue } // Strip hex addresses. fn = hexAddrRe.ReplaceAllString(fn, "") // Strip C++ template parameters. fn = templateParamRe.ReplaceAllString(fn, "<>") // Strip ABI tags. fn = abiTagRe.ReplaceAllString(fn, "") // Clean up whitespace. fn = strings.TrimSpace(fn) // Strip paths to just filename. file := f.File if file != "" { file = filepath.Base(file) } if fn == "" { continue } result = append(result, NormalizedFrame{ Function: fn, File: file, }) if len(result) >= maxFrames { break } } return result } func isRuntimeFrame(fn string) bool { for _, prefix := range runtimePrefixes { if strings.HasPrefix(fn, prefix) { return true } } return false }