From 880e3749083d7e48b021631402ef3da7bddf02d6 Mon Sep 17 00:00:00 2001 From: ahmadalguydi Date: Wed, 5 Aug 2026 18:03:26 +0300 Subject: [PATCH 1/2] fix: keep semicolons in xctrace symbols --- src/lib.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ac94e5b..8bc0389 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -495,6 +495,13 @@ fn print_command(cmd: &Command, verbose: bool) { } } +#[cfg(target_os = "macos")] +fn normalize_demangled_symbol(symbol: String) -> String { + // Folded stacks use semicolons as frame separators. Keep semicolons that + // occur inside Rust array types from being interpreted as extra frames. + symbol.replace(';', ":") +} + #[cfg(target_os = "macos")] fn demangle_stream(input: &mut R, output: &mut W) -> std::io::Result<()> { let mut reader = quick_xml::Reader::from_reader(input); @@ -521,7 +528,7 @@ fn demangle_stream(input: &mut R, output: &mut W) -> std:: let mangled = String::from_utf8_lossy(attr.value.as_ref()); if let Ok(demangled) = try_demangle(&mangled) { - let demangled = format!("{demangled:#}"); + let demangled = normalize_demangled_symbol(format!("{demangled:#}")); attr.value = match quick_xml::escape::escape(demangled) { Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()), Cow::Owned(s) => Cow::Owned(s.into_bytes()), @@ -537,6 +544,19 @@ fn demangle_stream(input: &mut R, output: &mut W) -> std:: Ok(()) } +#[cfg(all(test, target_os = "macos"))] +mod tests { + use super::normalize_demangled_symbol; + + #[test] + fn keeps_array_types_inside_one_folded_frame() { + assert_eq!( + normalize_demangled_symbol("foo::<[u8; 2]>()".to_owned()), + "foo::<[u8: 2]>()" + ); + } +} + pub fn generate_flamegraph_for_workload(workload: Workload, opts: Options) -> anyhow::Result<()> { // Handle SIGINT with an empty handler. This has the // implicit effect of allowing the signal to reach the From 48b191ead6436ca3f2b1a338cd3af1c0ab90c52d Mon Sep 17 00:00:00 2001 From: ahmadalguydi Date: Sat, 8 Aug 2026 02:21:04 +0300 Subject: [PATCH 2/2] refactor: test xctrace normalization through XML --- src/lib.rs | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8bc0389..c678310 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -495,13 +495,6 @@ fn print_command(cmd: &Command, verbose: bool) { } } -#[cfg(target_os = "macos")] -fn normalize_demangled_symbol(symbol: String) -> String { - // Folded stacks use semicolons as frame separators. Keep semicolons that - // occur inside Rust array types from being interpreted as extra frames. - symbol.replace(';', ":") -} - #[cfg(target_os = "macos")] fn demangle_stream(input: &mut R, output: &mut W) -> std::io::Result<()> { let mut reader = quick_xml::Reader::from_reader(input); @@ -528,7 +521,9 @@ fn demangle_stream(input: &mut R, output: &mut W) -> std:: let mangled = String::from_utf8_lossy(attr.value.as_ref()); if let Ok(demangled) = try_demangle(&mangled) { - let demangled = normalize_demangled_symbol(format!("{demangled:#}")); + // Folded stacks use semicolons as frame separators. Keep semicolons that + // occur inside Rust array types from being interpreted as extra frames. + let demangled = format!("{demangled:#}").replace(';', ":"); attr.value = match quick_xml::escape::escape(demangled) { Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()), Cow::Owned(s) => Cow::Owned(s.into_bytes()), @@ -544,19 +539,6 @@ fn demangle_stream(input: &mut R, output: &mut W) -> std:: Ok(()) } -#[cfg(all(test, target_os = "macos"))] -mod tests { - use super::normalize_demangled_symbol; - - #[test] - fn keeps_array_types_inside_one_folded_frame() { - assert_eq!( - normalize_demangled_symbol("foo::<[u8; 2]>()".to_owned()), - "foo::<[u8: 2]>()" - ); - } -} - pub fn generate_flamegraph_for_workload(workload: Workload, opts: Options) -> anyhow::Result<()> { // Handle SIGINT with an empty handler. This has the // implicit effect of allowing the signal to reach the @@ -838,3 +820,24 @@ impl FlamegraphOptions { options } } + +#[cfg(all(test, target_os = "macos"))] +mod tests { + use std::io::Cursor; + + use super::demangle_stream; + + #[test] + fn keeps_array_types_inside_one_folded_frame() { + let mut input = + Cursor::new(br#""#); + let mut output = Vec::new(); + + demangle_stream(&mut input, &mut output).unwrap(); + + assert_eq!( + String::from_utf8(output).unwrap(), + r#""# + ); + } +}