aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/report/linux.go
diff options
context:
space:
mode:
authorGrigory Bazilevich <g.bazilevich@ispras.ru>2025-10-27 18:35:41 +0300
committerGrigory Bazilevich <g.bazilevich@ispras.ru>2026-03-12 11:30:09 +0300
commit12aee2a11f4d9f364ce06451b87e3076bf0bf0b7 (patch)
treeaddae0fb2605be19ad5f2ec5a9ef9e50777cafb1 /pkg/report/linux.go
parent3e8f9cea1b2feb873fbad88a55ae8091e0c09e84 (diff)
pkg/report: ignore timer-related and mark_buffer_dirty frames
Fix corresponding test, remove incorrect tests Signed-off-by: Alexey Khoroshilov <khoroshilov@ispras.ru> Signed-off-by: Denis Efremov <efremov@ispras.ru>
Diffstat (limited to 'pkg/report/linux.go')
-rw-r--r--pkg/report/linux.go628
1 files changed, 326 insertions, 302 deletions
diff --git a/pkg/report/linux.go b/pkg/report/linux.go
index 69e674836..ab555f3b3 100644
--- a/pkg/report/linux.go
+++ b/pkg/report/linux.go
@@ -257,7 +257,8 @@ func (ctx *linux) reportMinLines(oopsLine []byte) int {
// Yes, it is complex, but all state and logic are tightly coupled. It's unclear how to simplify it.
// nolint: gocyclo
func (ctx *linux) findReport(output []byte, oops *oops, startPos int, context string, useQuestionable bool) (
- endPos, reportEnd int, report []byte, prefix [][]byte) {
+ endPos, reportEnd int, report []byte, prefix [][]byte,
+) {
// Prepend 5 lines preceding start of the report,
// they can contain additional info related to the report.
maxPrefix := 5
@@ -523,7 +524,8 @@ func (line linuxBacktraceLine) Assemble() []byte {
}
func symbolizeLine(symbFunc func(bin string, pc uint64) ([]symbolizer.Frame, error), ctx *linux,
- parsed linuxBacktraceLine) []linuxBacktraceLine {
+ parsed linuxBacktraceLine,
+) []linuxBacktraceLine {
symb := ctx.symbols[parsed.ModName][parsed.Name]
if len(symb) == 0 {
return []linuxBacktraceLine{parsed}
@@ -803,8 +805,10 @@ func (ctx *linux) extractGuiltyFileRaw(title string, report []byte) string {
// we would need to ignore too many files and that would be fragile.
// So instead we try to extract guilty file starting from the known
// interrupt entry point first.
- for _, interruptEnd := range []string{"apic_timer_interrupt+0x",
- "el1h_64_irq+0x", "Exception stack"} {
+ for _, interruptEnd := range []string{
+ "apic_timer_interrupt+0x",
+ "el1h_64_irq+0x", "Exception stack",
+ } {
if pos := bytes.Index(report, []byte(interruptEnd)); pos != -1 {
if file := ctx.extractGuiltyFileImpl(report[pos:]); file != "" {
return file
@@ -1031,9 +1035,11 @@ func linuxHangTaskFrameExtractor(frames []string) (string, int) {
}
}
}
- skip := []string{"sched", "_lock", "_slowlock", "down", "rwsem", "completion", "kthread",
+ skip := []string{
+ "sched", "_lock", "_slowlock", "down", "rwsem", "completion", "kthread",
"wait", "synchronize", "context_switch", "__switch_to", "cancel_delayed_work",
- "rcu_barrier"}
+ "rcu_barrier",
+ }
nextFrame:
for i, frame := range frames {
for _, ignore := range skip {
@@ -1147,293 +1153,302 @@ var linuxCorruptedTitles = []*regexp.Regexp{
// In riscv, if show_regs() is called from kernel space, the stack is dumped without a proper indicator. However a
// missing stack trace does not necessarily mean the log is corrupted. Match the last line printed by show_regs(),
// before the stack dump.
-var riscvSpecialStackStart = regexp.MustCompile(`status: [0-9a-f]{16} badaddr: [0-9a-f]{16} cause: [0-9a-f]{16}`)
-var linuxStackParams = &stackParams{
- stackStartRes: []*regexp.Regexp{
- regexp.MustCompile(`Call (?:T|t)race`),
- regexp.MustCompile(`Allocated:`),
- regexp.MustCompile(`Allocated by task [0-9]+:`),
- regexp.MustCompile(`Freed:`),
- regexp.MustCompile(`Freed by task [0-9]+:`),
- // Match 'backtrace:', but exclude 'stack backtrace:'
- // Also match optional crc hash for KMEMLEAK reports.
- regexp.MustCompile(`[^k] backtrace(?: \(crc [[:xdigit:]]*\))?:`),
- regexp.MustCompile(`Backtrace:`),
- regexp.MustCompile(`Uninit was stored to memory at`),
- // A special stack trace for RISC-V is handled separately.
- riscvSpecialStackStart,
- },
- frameRes: []*regexp.Regexp{
- compile("^ *(?:{{PC}} ){0,2}{{FUNC}}"),
- // Arm is totally different.
- // Extract both current and next frames. This is needed for the top
- // frame which is present only in LR register which we don't parse.
- compile(`^ *{{PC}} \(([a-zA-Z0-9_.]+)\) from {{PC}} \({{FUNC}}`),
- },
- skipPatterns: []string{
- "__sanitizer",
- "__asan",
- "kasan",
- "hwasan",
- "__msan",
- "kmsan",
- "kcsan_setup_watchpoint",
- "check_memory_region",
- "check_heap_object",
- "check_object",
- "read_word_at_a_time",
- "(read|write)_once_.*nocheck",
- "print_address_description",
- "panic",
- "invalid_op",
- "report_bug",
- "fixup_bug",
- "print_report",
- "print_usage_bug",
- "do_error",
- "invalid_op",
- `_trap$|do_trap`,
- "show_stack",
- "dump_stack",
- "walk_stack",
- "dump_backtrace",
- "warn_slowpath",
- "warn_alloc",
- "warn_bogus",
- "__warn",
- "alloc_page",
- "k?v?(?:m|z|c)alloc",
- "krealloc",
- "kmem_cache",
- "allocate_slab",
- "__alloc_frozen_pages_noprof",
- "folio_(?:alloc|unlock)",
- "filemap_alloc_folio",
- "__filemap_get_folio",
- "find_or_create_page",
- "do_read_cache_folio",
- "read_cache_page",
- "pagecache_get_page",
- "grab_cache_page_write_begin",
- "slab_",
- "debug_object",
- "timer_is_static_object",
- "work_is_static_object",
- "__might_fault",
- "print_unlock",
- "imbalance_bug",
- "lockdep",
- "bh_enable",
- "bh_disable",
- "perf_trace",
- "lock_acquire",
- "lock_release",
- "lock_class",
- "mark_lock",
- "(reacquire|mark)_held_locks",
- "raw_spin_rq",
- "spin_lock",
- "spin_trylock",
- "spin_unlock",
- "read_lock",
- "read_trylock",
- "write_lock",
- "write_trylock",
- "read_unlock",
- "write_unlock",
- "^down$",
- "down_read",
- "down_write",
- "down_read_trylock",
- "down_write_trylock",
- "down_trylock",
- "up_read",
- "up_write",
- "^mutex_",
- "^__mutex_",
- "^rt_mutex_",
- "owner_on_cpu",
- "osq_lock",
- "osq_unlock",
- "atomic(64)?_(dec|inc|read|set|or|xor|and|add|sub|fetch|xchg|cmpxchg|try)",
- "(set|clear|change|test)_bit",
- "__wake_up",
- "^refcount_",
- "^kref_",
- "ref_tracker",
- "seqprop_assert",
- "memcpy",
- "memcmp",
- "memset",
- "memchr",
- "memmove",
- "memdup",
- "strcmp",
- "strncmp",
- "strcpy",
- "strlcpy",
- "strncpy",
- "strscpy",
- "strlen",
- "strstr",
- "strnstr",
- "strnlen",
- "strchr",
- "strdup",
- "strndup",
- "copy_to_user",
- "copy_from_user",
- "copy_to_iter",
- "copy_from_iter",
- "copy_page_to_iter",
- "copy_page_from_iter",
- "copy_folio_to_iter",
- "^copyin$",
- "^copyout$",
- "put_user",
- "get_user",
- "might_fault",
- "might_sleep",
- "list_add",
- "list_del",
- "list_replace",
- "list_move",
- "list_splice",
- "^rb_",
- "^__rb_",
- "_indirect_thunk_", // retpolines
- "string",
- "pointer",
- "snprintf",
- "scnprintf",
- "kasprintf",
- "kvasprintf",
- "printk",
- "va_format",
- "dev_info",
- "dev_notice",
- "dev_warn",
- "dev_err",
- "dev_alert",
- "dev_crit",
- "dev_emerg",
- "program_check_exception",
- "program_check_common",
- "del_timer",
- "flush_work",
- "__cancel_work_timer",
- "cancel_work_sync",
- "try_to_grab_pending",
- "flush_workqueue",
- "drain_workqueue",
- "destroy_workqueue",
- "queue_work",
- "finish_wait",
- "kthread_stop",
- "kobject_",
- "add_uevent_var",
- "get_device_parent",
- "device_add",
- "device_del",
- "device_unregister",
- "device_destroy",
- "device_release",
- "devres_release_all",
- "hwrng_unregister",
- "i2c_del_adapter",
- "__unregister_client",
- "device_for_each_child",
- "rollback_registered",
- "unregister_netdev",
- "sysfs_remove",
- "device_remove_file",
- "tty_unregister_device",
- "dummy_urb_enqueue",
- "usb_kill_urb",
- "usb_kill_anchored_urbs",
- "usb_control_msg",
- "usb_hcd_submit_urb",
- "usb_submit_urb",
- "^complete$",
- "wait_for_completion",
- "^kv?free$",
- "kfree_skb",
- "readb$",
- "readw$",
- "readl$",
- "readq$",
- "writeb$",
- "writew$",
- "writel$",
- "writeq$",
- "logic_in",
- "logic_out",
- "^crc\\d+",
- "crc_itu_t",
- "__might_resched",
- "assertfail",
- "^iput$",
- "^iput_final$",
- "^ihold$",
- "hex_dump_to_buffer",
- "print_hex_dump",
- "^klist_",
- "(trace|lockdep)_(hard|soft)irq",
- "^(un)?lock_page",
- "stack_trace_consume_entry",
- "arch_stack_walk",
- "stack_trace_save",
- "insert_work",
- "__queue_delayed_work",
- "queue_delayed_work_on",
- "ida_free",
- // arm64 translation exception handling path.
- "do_(kernel|translation)_fault",
- "do_mem_abort",
- "el1_abort",
- "el1h_64_sync(?:_handler)?",
- "print_tainted",
- "xas_(?:start|load|find)",
- "find_lock_entries",
- "truncate_inode_pages_range",
- "__phys_addr",
- "__fortify_report",
- "cleanup_srcu_struct",
- "rhashtable_lookup",
- "extract_(user|iter)_to_sg",
- "drop_nlink",
- "^get_taint$",
- "^put_device$",
- "lock_timer_base",
- "__timer_delete_sync",
- "sk_stop_timer_sync",
- "__mod_timer",
- "fast_dput",
- "dput",
- },
- corruptedLines: []*regexp.Regexp{
- // Fault injection stacks are frequently intermixed with crash reports.
- // Note: the actual symbol can have all kinds of weird suffixes like ".isra.7", ".cold" or ".isra.56.cold.74".
- compile(`^( \[\<?(?:0x)?[0-9a-f]+\>?\])? should_fail(slab)?(\.[a-z0-9.]+)?\+0x`),
- },
- stripFramePrefixes: []string{
- "SYSC_",
- "SyS_",
- "sys_",
- "__x64_",
- "__ia32_",
- "__arm64_",
- "____sys_",
- "___sys_",
- "__sys_",
- "__se_",
- "__se_sys_",
- "__do_sys_",
- "compat_SYSC_",
- "compat_SyS_",
- "ksys_",
- },
-}
+var (
+ riscvSpecialStackStart = regexp.MustCompile(`status: [0-9a-f]{16} badaddr: [0-9a-f]{16} cause: [0-9a-f]{16}`)
+ linuxStackParams = &stackParams{
+ stackStartRes: []*regexp.Regexp{
+ regexp.MustCompile(`Call (?:T|t)race`),
+ regexp.MustCompile(`Allocated:`),
+ regexp.MustCompile(`Allocated by task [0-9]+:`),
+ regexp.MustCompile(`Freed:`),
+ regexp.MustCompile(`Freed by task [0-9]+:`),
+ // Match 'backtrace:', but exclude 'stack backtrace:'
+ // Also match optional crc hash for KMEMLEAK reports.
+ regexp.MustCompile(`[^k] backtrace(?: \(crc [[:xdigit:]]*\))?:`),
+ regexp.MustCompile(`Backtrace:`),
+ regexp.MustCompile(`Uninit was stored to memory at`),
+ // A special stack trace for RISC-V is handled separately.
+ riscvSpecialStackStart,
+ },
+ frameRes: []*regexp.Regexp{
+ compile("^ *(?:{{PC}} ){0,2}{{FUNC}}"),
+ // Arm is totally different.
+ // Extract both current and next frames. This is needed for the top
+ // frame which is present only in LR register which we don't parse.
+ compile(`^ *{{PC}} \(([a-zA-Z0-9_.]+)\) from {{PC}} \({{FUNC}}`),
+ },
+ skipPatterns: []string{
+ "__sanitizer",
+ "__asan",
+ "kasan",
+ "hwasan",
+ "__msan",
+ "kmsan",
+ "kcsan_setup_watchpoint",
+ "check_memory_region",
+ "check_heap_object",
+ "check_object",
+ "read_word_at_a_time",
+ "(read|write)_once_.*nocheck",
+ "print_address_description",
+ "panic",
+ "invalid_op",
+ "report_bug",
+ "fixup_bug",
+ "print_report",
+ "print_usage_bug",
+ "do_error",
+ "invalid_op",
+ `_trap$|do_trap`,
+ "show_stack",
+ "dump_stack",
+ "walk_stack",
+ "dump_backtrace",
+ "warn_slowpath",
+ "warn_alloc",
+ "warn_bogus",
+ "__warn",
+ "alloc_page",
+ "k?v?(?:m|z|c)alloc",
+ "krealloc",
+ "kmem_cache",
+ "allocate_slab",
+ "__alloc_frozen_pages_noprof",
+ "folio_(?:alloc|unlock)",
+ "filemap_alloc_folio",
+ "__filemap_get_folio",
+ "find_or_create_page",
+ "do_read_cache_folio",
+ "read_cache_page",
+ "pagecache_get_page",
+ "grab_cache_page_write_begin",
+ "slab_",
+ "debug_object",
+ "timer_is_static_object",
+ "work_is_static_object",
+ "__might_fault",
+ "print_unlock",
+ "imbalance_bug",
+ "lockdep",
+ "bh_enable",
+ "bh_disable",
+ "perf_trace",
+ "lock_acquire",
+ "lock_release",
+ "lock_class",
+ "mark_lock",
+ "(reacquire|mark)_held_locks",
+ "raw_spin_rq",
+ "spin_lock",
+ "spin_trylock",
+ "spin_unlock",
+ "read_lock",
+ "read_trylock",
+ "write_lock",
+ "write_trylock",
+ "read_unlock",
+ "write_unlock",
+ "^down$",
+ "down_read",
+ "down_write",
+ "down_read_trylock",
+ "down_write_trylock",
+ "down_trylock",
+ "up_read",
+ "up_write",
+ "^mutex_",
+ "^__mutex_",
+ "^rt_mutex_",
+ "owner_on_cpu",
+ "osq_lock",
+ "osq_unlock",
+ "atomic(64)?_(dec|inc|read|set|or|xor|and|add|sub|fetch|xchg|cmpxchg|try)",
+ "(set|clear|change|test)_bit",
+ "__wake_up",
+ "^refcount_",
+ "^kref_",
+ "ref_tracker",
+ "seqprop_assert",
+ "memcpy",
+ "memcmp",
+ "memset",
+ "memchr",
+ "memmove",
+ "memdup",
+ "strcmp",
+ "strncmp",
+ "strcpy",
+ "strlcpy",
+ "strncpy",
+ "strscpy",
+ "strlen",
+ "strstr",
+ "strnstr",
+ "strnlen",
+ "strchr",
+ "strdup",
+ "strndup",
+ "copy_to_user",
+ "copy_from_user",
+ "copy_to_iter",
+ "copy_from_iter",
+ "copy_page_to_iter",
+ "copy_page_from_iter",
+ "copy_folio_to_iter",
+ "^copyin$",
+ "^copyout$",
+ "put_user",
+ "get_user",
+ "might_fault",
+ "might_sleep",
+ "list_add",
+ "list_del",
+ "list_replace",
+ "list_move",
+ "list_splice",
+ "^rb_",
+ "^__rb_",
+ "_indirect_thunk_", // retpolines
+ "string",
+ "pointer",
+ "snprintf",
+ "scnprintf",
+ "kasprintf",
+ "kvasprintf",
+ "printk",
+ "va_format",
+ "dev_info",
+ "dev_notice",
+ "dev_warn",
+ "dev_err",
+ "dev_alert",
+ "dev_crit",
+ "dev_emerg",
+ "program_check_exception",
+ "program_check_common",
+ "del_timer",
+ "flush_work",
+ "cancel_work_sync",
+ "try_to_grab_pending",
+ "flush_workqueue",
+ "drain_workqueue",
+ "destroy_workqueue",
+ "queue_work",
+ "finish_wait",
+ "kthread_stop",
+ "kobject_",
+ "add_uevent_var",
+ "get_device_parent",
+ "device_add",
+ "device_del",
+ "device_unregister",
+ "device_destroy",
+ "device_release",
+ "devres_release_all",
+ "hwrng_unregister",
+ "i2c_del_adapter",
+ "__unregister_client",
+ "device_for_each_child",
+ "rollback_registered",
+ "unregister_netdev",
+ "sysfs_remove",
+ "device_remove_file",
+ "tty_unregister_device",
+ "dummy_urb_enqueue",
+ "usb_kill_urb",
+ "usb_kill_anchored_urbs",
+ "usb_control_msg",
+ "usb_hcd_submit_urb",
+ "usb_submit_urb",
+ "^complete$",
+ "wait_for_completion",
+ "^kv?free$",
+ "kfree_skb",
+ "readb$",
+ "readw$",
+ "readl$",
+ "readq$",
+ "writeb$",
+ "writew$",
+ "writel$",
+ "writeq$",
+ "logic_in",
+ "logic_out",
+ "^crc\\d+",
+ "crc_itu_t",
+ "__might_resched",
+ "assertfail",
+ "^iput$",
+ "^iput_final$",
+ "^ihold$",
+ "hex_dump_to_buffer",
+ "print_hex_dump",
+ "^klist_",
+ "(trace|lockdep)_(hard|soft)irq",
+ "^(un)?lock_page",
+ "stack_trace_consume_entry",
+ "arch_stack_walk",
+ "stack_trace_save",
+ "insert_work",
+ "__queue_delayed_work",
+ "queue_delayed_work_on",
+ "ida_free",
+ // arm64 translation exception handling path.
+ "do_(kernel|translation)_fault",
+ "do_mem_abort",
+ "el1_abort",
+ "el1h_64_sync(?:_handler)?",
+ "print_tainted",
+ "xas_(?:start|load|find)",
+ "find_lock_entries",
+ "truncate_inode_pages_range",
+ "__phys_addr",
+ "__fortify_report",
+ "cleanup_srcu_struct",
+ "rhashtable_lookup",
+ "extract_(user|iter)_to_sg",
+ "drop_nlink",
+ "^get_taint$",
+ "^put_device$",
+ "lock_timer_base",
+ "__timer_delete_sync",
+ "sk_stop_timer_sync",
+ "enqueue_timer",
+ "internal_add_timer",
+ "__mod_timer",
+ "fast_dput",
+ "dput",
+ "mod_timer",
+ "queue_delayed_work",
+ "detach_timer",
+ "detach_if_pending",
+ "__cancel_work_timer",
+ "mark_buffer_dirty",
+ },
+ corruptedLines: []*regexp.Regexp{
+ // Fault injection stacks are frequently intermixed with crash reports.
+ // Note: the actual symbol can have all kinds of weird suffixes like ".isra.7", ".cold" or ".isra.56.cold.74".
+ compile(`^( \[\<?(?:0x)?[0-9a-f]+\>?\])? should_fail(slab)?(\.[a-z0-9.]+)?\+0x`),
+ },
+ stripFramePrefixes: []string{
+ "SYSC_",
+ "SyS_",
+ "sys_",
+ "__x64_",
+ "__ia32_",
+ "__arm64_",
+ "____sys_",
+ "___sys_",
+ "__sys_",
+ "__se_",
+ "__se_sys_",
+ "__do_sys_",
+ "compat_SYSC_",
+ "compat_SyS_",
+ "ksys_",
+ },
+ }
+)
func warningStackFmt(skip ...string) *stackFmt {
return &stackFmt{
@@ -1528,7 +1543,8 @@ var linuxOopses = append([]*oops{
"alloc_skb", "netlink_ack", "netlink_rcv_skb",
// Encryption routines are the place where we hit the bug, but
// the generic code is a bad candidate for bug titles.
- "_encrypt$", "^(?:crypto|cipher|drbg|rng)_"},
+ "_encrypt$", "^(?:crypto|cipher|drbg|rng)_",
+ },
},
noStackTrace: true,
},
@@ -1719,9 +1735,11 @@ var linuxOopses = append([]*oops{
compile(`page last allocated`),
parseStackTrace,
},
- skip: []string{"(free|put|get|update|release)_page",
+ skip: []string{
+ "(free|put|get|update|release)_page",
"free_unref", "^_*folio", "truncate_inode_pages",
- "page_frag_free", "alloc", "vmap", "page_owner"},
+ "page_frag_free", "alloc", "vmap", "page_owner",
+ },
},
},
{
@@ -1751,8 +1769,10 @@ var linuxOopses = append([]*oops{
linuxCallTrace,
parseStackTrace,
},
- skip: []string{"dump_stack", "preemption", "preempt", "debug_",
- "processor_id", "this_cpu"},
+ skip: []string{
+ "dump_stack", "preemption", "preempt", "debug_",
+ "processor_id", "this_cpu",
+ },
},
},
{
@@ -1770,9 +1790,11 @@ var linuxOopses = append([]*oops{
compile("backtrace(?: \\(crc [[:xdigit:]]*\\))?:"),
parseStackTrace,
},
- skip: []string{"kmemleak", "mmap", "kmem", "slab", "alloc", "create_object",
+ skip: []string{
+ "kmemleak", "mmap", "kmem", "slab", "alloc", "create_object",
"idr_get", "list_lru_init", "kasprintf", "kvasprintf",
- "pcpu_create", "strdup", "strndup", "memdup"},
+ "pcpu_create", "strdup", "strndup", "memdup",
+ },
},
},
{
@@ -1920,8 +1942,10 @@ var linuxOopses = append([]*oops{
// These workqueue functions take locks associated with work items.
// All deadlocks observed in these functions are
// work-item-subsystem-related.
- skip: []string{"process_one_work", "flush_workqueue",
- "drain_workqueue", "destroy_workqueue"},
+ skip: []string{
+ "process_one_work", "flush_workqueue",
+ "drain_workqueue", "destroy_workqueue",
+ },
},
},
{