feat: warn when libc debug info is not found#302
Conversation
When libc-dbg is not installed, libc frames in perf flamegraphs appear as bare hex addresses, making comparisons unusable. This adds a warning during walltime profile processing when libc is loaded but has no DWARF debug info, suggesting users install libc6-dbg.
There was a problem hiding this comment.
Pull request overview
Adds a targeted warning during wall-time perf artifact generation when libc is loaded but DWARF-based debug info extraction fails, to help users understand why libc frames may be poorly symbolicated in flamegraphs.
Changes:
- Introduces
is_libc_path()to detect glibc-like libc filenames while avoiding common false positives (e.g.,libc-client.so). - Introduces
warn_missing_libc_debug_info()to warn when libc symbols exist but DWARF debug info wasn’t extracted. - Invokes the warning from
save_debug_info()immediately after debug info extraction.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "libc debug info not found for {}. Flamegraphs may contain \ | ||
| unsymbolicated libc frames. Install debug symbols \ | ||
| (e.g., `apt install libc6-dbg`) to fix this.", |
There was a problem hiding this comment.
The warning suggests installing libc6-dbg via apt will fix missing DWARF, but debug_info_by_path() only attempts to read DWARF sections from the mapped ELF at path (it doesn't resolve .gnu_debuglink/build-id or /usr/lib/debug files). On Debian/Ubuntu, installing libc6-dbg typically provides a separate debug file, so this message may be misleading/unactionable (and apt is distro-specific). Consider either (a) implementing lookup of external debug files for libc (debuglink/build-id) or (b) adjusting the warning text to be OS/distro-agnostic and accurately describe what data is missing and where the tool looks for it.
| "libc debug info not found for {}. Flamegraphs may contain \ | |
| unsymbolicated libc frames. Install debug symbols \ | |
| (e.g., `apt install libc6-dbg`) to fix this.", | |
| "No DWARF debug info was found in the mapped libc ELF at {}. \ | |
| Flamegraphs may contain unsymbolicated libc frames. \ | |
| This tool currently reads debug info from the mapped ELF path only.", |
| || (filename.starts_with("libc-") | ||
| && filename.as_bytes().get(5).is_some_and(u8::is_ascii_digit)) | ||
| } | ||
|
|
There was a problem hiding this comment.
is_libc_path() introduces non-trivial filename matching logic, but there are no unit tests for the accepted/rejected patterns (e.g., libc.so.6, libc-2.31.so should match; libc-client.so should not). Adding a small #[cfg(test)] module with table-driven cases would help prevent future regressions and clarify the intended matching behavior.
| #[cfg(test)] | |
| mod tests { | |
| use super::is_libc_path; | |
| use std::path::Path; | |
| #[test] | |
| fn is_libc_path_accepts_expected_libc_filenames() { | |
| let cases = [ | |
| "/lib/x86_64-linux-gnu/libc.so", | |
| "/lib/x86_64-linux-gnu/libc.so.6", | |
| "/usr/lib/libc-2.31.so", | |
| "libc-2.so", | |
| ]; | |
| for case in cases { | |
| assert!(is_libc_path(Path::new(case)), "expected match for {case}"); | |
| } | |
| } | |
| #[test] | |
| fn is_libc_path_rejects_non_libc_filenames() { | |
| let cases = [ | |
| "/usr/lib/libc-client.so", | |
| "/usr/lib/libcrypt.so", | |
| "/usr/lib/libc-.so", | |
| "/usr/lib/libc-a.so", | |
| "/usr/lib/not-libc.so.6", | |
| "/", | |
| "", | |
| ]; | |
| for case in cases { | |
| assert!(!is_libc_path(Path::new(case)), "unexpected match for {case}"); | |
| } | |
| } | |
| } |
| || (filename.starts_with("libc-") | ||
| && filename.as_bytes().get(5).is_some_and(u8::is_ascii_digit)) |
There was a problem hiding this comment.
The filename.as_bytes().get(5) check relies on the hard-coded index immediately after the "libc-" prefix, which is a bit opaque. Consider rewriting using strip_prefix("libc-") + bytes().next() (or similar) to make the intent clearer and avoid manual indexing.
| || (filename.starts_with("libc-") | |
| && filename.as_bytes().get(5).is_some_and(u8::is_ascii_digit)) | |
| || filename | |
| .strip_prefix("libc-") | |
| .and_then(|rest| rest.bytes().next()) | |
| .is_some_and(|b| b.is_ascii_digit()) |
Summary
libc-dbginstalled, libc frames in flamegraphs appear as bare hex addresses, making diff comparisons unusableChanges
is_libc_path()helper to match libc filenames (libc.so.6,libc-2.31.so, etc.) while excluding unrelated libraries (libc-client.so)warn_missing_libc_debug_info()that compares loaded modules against successfully-extracted debug info and warns for libc modules with missing DWARF datasave_debug_info()right after debug info extraction completesTest plan
libc6-dbginstalled — verify the warning appears in logslibc6-dbginstalled — verify no warning appearslibinstrument-hooks.sois deleted at exit) do not trigger the warning