diff --git a/Cargo.lock b/Cargo.lock index a4693dbe..f518c357 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -648,8 +648,6 @@ dependencies = [ [[package]] name = "framehop" version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0586ca77af938ae3d66a103d3082ac997b432e82e65d644be6ad2fa340f582d" dependencies = [ "arrayvec", "cfg-if", @@ -1257,6 +1255,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" +[[package]] +name = "linux-perf-data" +version = "0.11.0" +dependencies = [ + "byteorder", + "linear-map", + "linux-perf-event-reader", + "memchr", + "prost", + "prost-derive", + "thiserror 2.0.12", +] + [[package]] name = "linux-perf-data" version = "0.11.0" @@ -2188,7 +2199,7 @@ dependencies = [ "indexmap", "lazy_static", "libc", - "linux-perf-data", + "linux-perf-data 0.11.0", "log", "mach2", "memchr", @@ -2277,7 +2288,7 @@ dependencies = [ "flate2", "futures", "gimli 0.32.0", - "linux-perf-data", + "linux-perf-data 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "lzma-rs", "macho-unwind-info", "memchr", diff --git a/samply/Cargo.toml b/samply/Cargo.toml index f4d369bc..b53af03c 100644 --- a/samply/Cargo.toml +++ b/samply/Cargo.toml @@ -12,10 +12,10 @@ readme = "README.md" [dependencies] fxprof-processed-profile = { version = "0.8", path = "../fxprof-processed-profile" } -# framehop = { path = "../../framehop" } -framehop = "0.14" -# linux-perf-data = { path = "../../linux-perf-data" } -linux-perf-data = "0.11" +framehop = { path = "../../framehop" } +#framehop = "0.14" +linux-perf-data = { path = "../../linux-perf-data" } +#linux-perf-data = "0.11" tokio = { version = "1.39", features = ["rt", "rt-multi-thread", "macros"] } tokio-util = "0.7.11" diff --git a/samply/src/import/perf.rs b/samply/src/import/perf.rs index 2387be54..02fcb472 100644 --- a/samply/src/import/perf.rs +++ b/samply/src/import/perf.rs @@ -10,7 +10,7 @@ use linux_perf_data::{linux_perf_event_reader, DsoInfo, DsoKey, PerfFileReader, use linux_perf_event_reader::EventRecord; use crate::linux_shared::{ - ConvertRegs, ConvertRegsAarch64, ConvertRegsX86_64, Converter, EventInterpretation, KnownEvent, + ConvertRegs, ConvertRegsAarch64, ConvertRegsX86_64, ConvertRegsArmhf, Converter, EventInterpretation, KnownEvent, MmapRangeOrVec, }; use crate::shared::prop_types::ProfileCreationProps; @@ -47,6 +47,17 @@ pub fn convert( profile_creation_props, ) } + Some("arm") => { + let cache = framehop::armhf::CacheArmhf::new(); + convert_impl::, ConvertRegsArmhf, _>( + perf_file, + file_mod_time, + binary_lookup_dirs, + aux_file_lookup_dirs, + cache, + profile_creation_props, + ) + } _ => { if arch != Some("x86_64") { eprintln!( diff --git a/samply/src/linux/profiler.rs b/samply/src/linux/profiler.rs index 24225c5e..b794074c 100644 --- a/samply/src/linux/profiler.rs +++ b/samply/src/linux/profiler.rs @@ -33,6 +33,9 @@ pub type ConvertRegsNative = crate::linux_shared::ConvertRegsX86_64; #[cfg(target_arch = "aarch64")] pub type ConvertRegsNative = crate::linux_shared::ConvertRegsAarch64; +#[cfg(target_arch = "arm")] +pub type ConvertRegsNative = crate::linux_shared::ConvertRegsArmhf; + pub fn run( recording_mode: RecordingMode, recording_props: RecordingProps, diff --git a/samply/src/linux_shared/convert_regs.rs b/samply/src/linux_shared/convert_regs.rs index 8cf99111..33673011 100644 --- a/samply/src/linux_shared/convert_regs.rs +++ b/samply/src/linux_shared/convert_regs.rs @@ -1,9 +1,10 @@ use framehop::aarch64::UnwindRegsAarch64; use framehop::x86_64::UnwindRegsX86_64; +use framehop::armhf::UnwindRegsArmhf; use linux_perf_data::linux_perf_event_reader; use linux_perf_event_reader::constants::{ PERF_REG_ARM64_LR, PERF_REG_ARM64_PC, PERF_REG_ARM64_SP, PERF_REG_ARM64_X29, PERF_REG_X86_BP, - PERF_REG_X86_IP, PERF_REG_X86_SP, + PERF_REG_X86_IP, PERF_REG_X86_SP,PERF_REG_ARM_PC, PERF_REG_ARM_LR, PERF_REG_ARM_SP, PERF_REG_ARM_R7 }; use linux_perf_event_reader::Regs; @@ -50,3 +51,26 @@ impl ConvertRegs for ConvertRegsAarch64 { | (1 << PERF_REG_ARM64_X29) } } + +pub struct ConvertRegsArmhf; +impl ConvertRegs for ConvertRegsArmhf { + type UnwindRegs = UnwindRegsArmhf; + fn convert_regs(regs: &Regs) -> (u64, u64, UnwindRegsArmhf) { + // println!("{:?}", regs); + let ip = regs.get(PERF_REG_ARM_PC).unwrap(); + let lr = regs.get(PERF_REG_ARM_LR).unwrap(); + let sp = regs.get(PERF_REG_ARM_SP).unwrap(); + // Note: This is x7 for thumb, x11 for arm mode. + let fp = regs.get(PERF_REG_ARM_R7).unwrap(); + // println!("Convert: lr, sp, fp: {:x} {:x} {:x}", lr, sp, fp); + let regs = UnwindRegsArmhf::new(lr, sp, fp); + (ip, sp, regs) + } + + fn regs_mask() -> u64 { + (1 << PERF_REG_ARM_PC) + | (1 << PERF_REG_ARM_LR) + | (1 << PERF_REG_ARM_SP) + | (1 << PERF_REG_ARM_R7) + } +} diff --git a/samply/src/linux_shared/converter.rs b/samply/src/linux_shared/converter.rs index 99930370..c529ac24 100644 --- a/samply/src/linux_shared/converter.rs +++ b/samply/src/linux_shared/converter.rs @@ -58,6 +58,71 @@ use crate::shared::unresolved_samples::{ }; use crate::shared::utils::open_file_with_fallback; +use byteorder::NativeEndian; +use byteorder::ByteOrder; +use std::mem; +use crate::linux_shared::converter::linux_perf_event_reader::RawData; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct RawDataU32<'a> { + swapped_endian: bool, + raw_data: RawData<'a>, +} + +pub fn is_swapped_endian() -> bool { + let mut buf = [0; 2]; + T::write_u16(&mut buf, 0x1234); + u16::from_ne_bytes(buf) != 0x1234 +} + +impl<'a> RawDataU32<'a> { + #[inline] + pub fn from_raw_data(raw_data: RawData<'a>) -> Self { + RawDataU32 { + raw_data, + swapped_endian: is_swapped_endian::(), + } + } + + pub fn is_empty(&self) -> bool { + self.raw_data.is_empty() + } + + pub fn len(&self) -> usize { + self.raw_data.len() / mem::size_of::() + } + + pub fn get(&self, index: usize) -> Option { + let offset = index * mem::size_of::(); + let mut data = self.raw_data; + data.skip(offset).ok()?; + let value = data.read_u32::().ok()?; + Some(if self.swapped_endian { + value.swap_bytes() + } else { + value + }) + } +} + +impl std::fmt::Debug for RawDataU32<'_> { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + let mut list = fmt.debug_list(); + let mut data = self.raw_data; + while let Ok(value) = data.read_u64::() { + let value = if self.swapped_endian { + value.swap_bytes() + } else { + value + }; + list.entry(&value); + } + + list.finish() + } +} + + pub struct Converter where U: Unwinder> + Default, @@ -642,9 +707,9 @@ where let stack_frame = match (is_first_frame, call_chain_return_addresses_are_preadjusted) { - (true, _) => StackFrame::InstructionPointer(address, mode), - (false, false) => StackFrame::ReturnAddress(address, mode), - (false, true) => StackFrame::AdjustedReturnAddress(address, mode), + (true, _) => StackFrame::InstructionPointer(address & 0x00000000_fffffffeu64, mode), + (false, false) => StackFrame::ReturnAddress(address & 0x00000000_fffffffeu64, mode), + (false, true) => StackFrame::AdjustedReturnAddress(address & 0x00000000_fffffffeu64, mode), }; stack.push(stack_frame); @@ -654,13 +719,16 @@ where // Append the user stack with the help of DWARF unwinding. if let (Some(regs), Some((user_stack, _))) = (&e.user_regs, e.user_stack) { - let ustack_bytes = RawDataU64::from_raw_data::(user_stack); + let ustack_bytes = RawDataU32::from_raw_data::(user_stack); let (pc, sp, regs) = C::convert_regs(regs); + // println!("pc: {:x}, sp: {:x}", pc, sp); let mut read_stack = |addr: u64| { // ustack_bytes has the stack bytes starting from the current stack pointer. let offset = addr.checked_sub(sp).ok_or(())?; - let index = usize::try_from(offset / 8).map_err(|_| ())?; - ustack_bytes.get(index).ok_or(()) + let index = usize::try_from(offset / 4).map_err(|_| ())?; + let result = ustack_bytes.get(index).ok_or(())?; + // println!("Stack: {:x} = {:x}", addr, result); + Ok(result as u64) }; // Unwind. @@ -676,10 +744,12 @@ where }; let stack_frame = match frame { FrameAddress::InstructionPointer(addr) => { - StackFrame::InstructionPointer(addr, StackMode::User) + // println!("Stack frame: {:x}", addr & 0x00000000_fffffffeu64); + StackFrame::InstructionPointer(addr & 0x00000000_fffffffeu64, StackMode::User) } FrameAddress::ReturnAddress(addr) => { - StackFrame::ReturnAddress(addr.into(), StackMode::User) + // println!("(+)Stack frame: {:x}", addr.get() & 0x00000000_fffffffeu64); + StackFrame::ReturnAddress(addr.get() & 0x00000000_fffffffeu64, StackMode::User) } }; stack.push(stack_frame); diff --git a/samply/src/linux_shared/mod.rs b/samply/src/linux_shared/mod.rs index 590e070e..204de191 100644 --- a/samply/src/linux_shared/mod.rs +++ b/samply/src/linux_shared/mod.rs @@ -16,7 +16,7 @@ mod thread; #[allow(unused)] pub mod vdso; -pub use convert_regs::{ConvertRegs, ConvertRegsAarch64, ConvertRegsX86_64}; +pub use convert_regs::{ConvertRegs, ConvertRegsAarch64, ConvertRegsX86_64, ConvertRegsArmhf}; pub use converter::Converter; #[allow(unused)] pub use event_interpretation::{EventInterpretation, KnownEvent, OffCpuIndicator};