From 134984143183f5cd88a3238bb7bb0e6b6b7032a7 Mon Sep 17 00:00:00 2001 From: Christian Jordan <svorre2304@gmail.com> Date: Sun, 24 Jul 2022 01:30:59 +0200 Subject: [PATCH 1/5] Separated platform specific implementation into separate files This is needed for windows support. Though the windows implementation is currently blank. It compiles and runs on windows and linux, but I haven't tested macos. --- Cargo.toml | 12 +- src/error.rs | 9 +- src/lib.rs | 5 +- src/platform/mod.rs | 19 ++ .../platform_nix}/addr_validate.rs | 0 src/platform/platform_nix/profiler.rs | 185 +++++++++++++++++ src/{ => platform/platform_nix}/timer.rs | 0 .../platform_windows/addr_validate.rs | 3 + src/platform/platform_windows/profiler.rs | 8 + src/platform/platform_windows/timer.rs | 55 +++++ src/profiler.rs | 190 +----------------- src/report.rs | 2 +- 12 files changed, 298 insertions(+), 190 deletions(-) create mode 100644 src/platform/mod.rs rename src/{ => platform/platform_nix}/addr_validate.rs (100%) create mode 100644 src/platform/platform_nix/profiler.rs rename src/{ => platform/platform_nix}/timer.rs (100%) create mode 100644 src/platform/platform_windows/addr_validate.rs create mode 100644 src/platform/platform_windows/profiler.rs create mode 100644 src/platform/platform_windows/timer.rs diff --git a/Cargo.toml b/Cargo.toml index 1c113036..ab625380 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ backtrace = { version = "0.3" } once_cell = "1.9" libc = "^0.2.66" log = "0.4" -nix = { version = "0.24", default-features = false, features = ["signal", "fs"] } + parking_lot = "0.12" tempfile = "3.1" thiserror = "1.0" @@ -37,7 +37,15 @@ inferno = { version = "0.11", default-features = false, features = ["nameattr"], prost = { version = "0.10", optional = true } prost-derive = { version = "0.10", optional = true } protobuf = { version = "2.0", optional = true } -criterion = {version = "0.3", optional = true} +criterion = { version = "0.3", optional = true } + +[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] +nix = { version = "0.24", default-features = false, features = [ + "signal", + "fs", +] } + +[target.'cfg(target_os = "windows")'.dependencies] [dependencies.symbolic-demangle] version = "9.0" diff --git a/src/error.rs b/src/error.rs index 6d5747ea..d487cf4d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,16 @@ // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. +// TODO Windows error is not finished #[derive(Debug, thiserror::Error)] pub enum Error { + #[cfg(target_os = "windows")] #[error("{0}")] - NixError(#[from] nix::Error), + OsError(i32), + + #[cfg(any(target_os = "linux", target_os = "macos"))] + #[error("{0}")] + OsError(#[from] nix::Error), + #[error("{0}")] IoError(#[from] std::io::Error), #[error("create profiler error")] diff --git a/src/lib.rs b/src/lib.rs index ed87e566..560ee79f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,7 +45,7 @@ pub const MAX_DEPTH: usize = 32; /// Define the MAX supported thread name length. TODO: make this variable mutable. pub const MAX_THREAD_NAME: usize = 16; -mod addr_validate; +mod platform; mod backtrace; mod collector; @@ -53,12 +53,11 @@ mod error; mod frames; mod profiler; mod report; -mod timer; -pub use self::addr_validate::validate; pub use self::collector::{Collector, HashCounter}; pub use self::error::{Error, Result}; pub use self::frames::{Frames, Symbol}; +pub use self::platform::addr_validate::validate; pub use self::profiler::{ProfilerGuard, ProfilerGuardBuilder}; pub use self::report::{Report, ReportBuilder, UnresolvedReport}; diff --git a/src/platform/mod.rs b/src/platform/mod.rs new file mode 100644 index 00000000..10cacd48 --- /dev/null +++ b/src/platform/mod.rs @@ -0,0 +1,19 @@ +#[cfg(any(target_os = "linux", target_os = "macos"))] +mod platform_nix { + pub mod addr_validate; + pub mod profiler; + pub mod timer; +} + +#[cfg(target_os = "windows")] +mod platform_windows { + pub mod addr_validate; + pub mod profiler; + pub mod timer; +} + +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub use platform_nix::*; + +#[cfg(target_os = "windows")] +pub use platform_windows::*; diff --git a/src/addr_validate.rs b/src/platform/platform_nix/addr_validate.rs similarity index 100% rename from src/addr_validate.rs rename to src/platform/platform_nix/addr_validate.rs diff --git a/src/platform/platform_nix/profiler.rs b/src/platform/platform_nix/profiler.rs new file mode 100644 index 00000000..9561193a --- /dev/null +++ b/src/platform/platform_nix/profiler.rs @@ -0,0 +1,185 @@ +use std::os::raw::c_int; +use std::time::SystemTime; + +use crate::backtrace::{Frame, Trace, TraceImpl}; +use smallvec::SmallVec; + +use nix::sys::signal; + +use crate::error::{Error, Result}; +use crate::profiler::PROFILER; +use crate::{MAX_DEPTH, MAX_THREAD_NAME}; + +pub fn register() -> Result<()> { + let handler = signal::SigHandler::SigAction(perf_signal_handler); + let sigaction = signal::SigAction::new( + handler, + signal::SaFlags::SA_SIGINFO, + signal::SigSet::empty(), + ); + unsafe { signal::sigaction(signal::SIGPROF, &sigaction) }?; + + Ok(()) +} +pub fn unregister() -> Result<()> { + let handler = signal::SigHandler::SigIgn; + unsafe { signal::signal(signal::SIGPROF, handler) }?; + + Ok(()) +} + +fn write_thread_name_fallback(current_thread: libc::pthread_t, name: &mut [libc::c_char]) { + let mut len = 0; + let mut base = 1; + + while current_thread as u128 > base && len < MAX_THREAD_NAME { + base *= 10; + len += 1; + } + + let mut index = 0; + while index < len && base > 1 { + base /= 10; + + name[index] = match (48 + (current_thread as u128 / base) % 10).try_into() { + Ok(digit) => digit, + Err(_) => { + log::error!("fail to convert thread_id to string"); + 0 + } + }; + + index += 1; + } +} + +#[cfg(not(all(any(target_os = "linux", target_os = "macos"), target_env = "gnu")))] +fn write_thread_name(current_thread: libc::pthread_t, name: &mut [libc::c_char]) { + write_thread_name_fallback(current_thread, name); +} + +#[cfg(all(any(target_os = "linux", target_os = "macos"), target_env = "gnu"))] +fn write_thread_name(current_thread: libc::pthread_t, name: &mut [libc::c_char]) { + let name_ptr = name as *mut [libc::c_char] as *mut libc::c_char; + let ret = unsafe { libc::pthread_getname_np(current_thread, name_ptr, MAX_THREAD_NAME) }; + + if ret != 0 { + write_thread_name_fallback(current_thread, name); + } +} + +struct ErrnoProtector(libc::c_int); + +impl ErrnoProtector { + fn new() -> Self { + unsafe { + #[cfg(target_os = "linux")] + { + let errno = *libc::__errno_location(); + Self(errno) + } + #[cfg(target_os = "macos")] + { + let errno = *libc::__error(); + Self(errno) + } + } + } +} + +impl Drop for ErrnoProtector { + fn drop(&mut self) { + unsafe { + #[cfg(target_os = "linux")] + { + *libc::__errno_location() = self.0; + } + #[cfg(target_os = "macos")] + { + *libc::__error() = self.0; + } + } + } +} + +#[no_mangle] +#[cfg_attr( + not(all(any(target_arch = "x86_64", target_arch = "aarch64"))), + allow(unused_variables) +)] +extern "C" fn perf_signal_handler( + _signal: c_int, + _siginfo: *mut libc::siginfo_t, + ucontext: *mut libc::c_void, +) { + let _errno = ErrnoProtector::new(); + + if let Some(mut guard) = PROFILER.try_write() { + if let Ok(profiler) = guard.as_mut() { + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] + if !ucontext.is_null() { + let ucontext: *mut libc::ucontext_t = ucontext as *mut libc::ucontext_t; + + #[cfg(all(target_arch = "x86_64", target_os = "linux"))] + let addr = + unsafe { (*ucontext).uc_mcontext.gregs[libc::REG_RIP as usize] as usize }; + + #[cfg(all(target_arch = "x86_64", target_os = "macos"))] + let addr = unsafe { + let mcontext = (*ucontext).uc_mcontext; + if mcontext.is_null() { + 0 + } else { + (*mcontext).__ss.__rip as usize + } + }; + + #[cfg(all(target_arch = "aarch64", target_os = "linux"))] + let addr = unsafe { (*ucontext).uc_mcontext.pc as usize }; + + #[cfg(all(target_arch = "aarch64", target_os = "macos"))] + let addr = unsafe { + let mcontext = (*ucontext).uc_mcontext; + if mcontext.is_null() { + 0 + } else { + (*mcontext).__ss.__pc as usize + } + }; + + if profiler.is_blocklisted(addr) { + return; + } + } + + let mut bt: SmallVec<[<TraceImpl as Trace>::Frame; MAX_DEPTH]> = + SmallVec::with_capacity(MAX_DEPTH); + let mut index = 0; + + let sample_timestamp: SystemTime = SystemTime::now(); + TraceImpl::trace(ucontext, |frame| { + let ip = Frame::ip(frame); + if profiler.is_blocklisted(ip) { + return false; + } + + if index < MAX_DEPTH { + bt.push(frame.clone()); + index += 1; + true + } else { + false + } + }); + + let current_thread = unsafe { libc::pthread_self() }; + let mut name = [0; MAX_THREAD_NAME]; + let name_ptr = &mut name as *mut [libc::c_char] as *mut libc::c_char; + + write_thread_name(current_thread, &mut name); + + let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) }; + profiler.sample(bt, name.to_bytes(), current_thread as u64, sample_timestamp); + } + } +} diff --git a/src/timer.rs b/src/platform/platform_nix/timer.rs similarity index 100% rename from src/timer.rs rename to src/platform/platform_nix/timer.rs diff --git a/src/platform/platform_windows/addr_validate.rs b/src/platform/platform_windows/addr_validate.rs new file mode 100644 index 00000000..20954778 --- /dev/null +++ b/src/platform/platform_windows/addr_validate.rs @@ -0,0 +1,3 @@ +pub fn validate() -> bool { + unimplemented!() +} diff --git a/src/platform/platform_windows/profiler.rs b/src/platform/platform_windows/profiler.rs new file mode 100644 index 00000000..4f65cc89 --- /dev/null +++ b/src/platform/platform_windows/profiler.rs @@ -0,0 +1,8 @@ +use crate::error::Result; + +pub fn register() -> Result<()> { + Ok(()) +} +pub fn unregister() -> Result<()> { + Ok(()) +} diff --git a/src/platform/platform_windows/timer.rs b/src/platform/platform_windows/timer.rs new file mode 100644 index 00000000..88335c62 --- /dev/null +++ b/src/platform/platform_windows/timer.rs @@ -0,0 +1,55 @@ +// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. + +use std::os::raw::c_int; +use std::time::{Duration, Instant, SystemTime}; + +pub struct Timer { + pub frequency: c_int, + pub start_time: SystemTime, + pub start_instant: Instant, +} + +impl Timer { + pub fn new(frequency: c_int) -> Timer { + Timer { + frequency, + start_time: SystemTime::now(), + start_instant: Instant::now(), + } + } + + /// Returns a `ReportTiming` struct having this timer's frequency and start + /// time; and the time elapsed since its creation as duration. + pub fn timing(&self) -> ReportTiming { + ReportTiming { + frequency: self.frequency, + start_time: self.start_time, + duration: self.start_instant.elapsed(), + } + } +} + +impl Drop for Timer { + fn drop(&mut self) {} +} + +/// Timing metadata for a collected report. +#[derive(Clone)] +pub struct ReportTiming { + /// Frequency at which samples were collected. + pub frequency: i32, + /// Collection start time. + pub start_time: SystemTime, + /// Collection duration. + pub duration: Duration, +} + +impl Default for ReportTiming { + fn default() -> Self { + Self { + frequency: 1, + start_time: SystemTime::UNIX_EPOCH, + duration: Default::default(), + } + } +} diff --git a/src/profiler.rs b/src/profiler.rs index 4bc72ad2..1023fcd3 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -1,10 +1,8 @@ // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. -use std::convert::TryInto; use std::os::raw::c_int; use std::time::SystemTime; -use nix::sys::signal; use once_cell::sync::Lazy; use parking_lot::RwLock; use smallvec::SmallVec; @@ -12,13 +10,14 @@ use smallvec::SmallVec; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use findshlibs::{Segment, SharedLibrary, TargetSharedLibrary}; -use crate::backtrace::{Frame, Trace, TraceImpl}; +use crate::backtrace::{Trace, TraceImpl}; use crate::collector::Collector; use crate::error::{Error, Result}; use crate::frames::UnresolvedFrames; +use crate::platform; +use crate::platform::timer::Timer; use crate::report::ReportBuilder; -use crate::timer::Timer; -use crate::{MAX_DEPTH, MAX_THREAD_NAME}; +use crate::MAX_DEPTH; pub(crate) static PROFILER: Lazy<RwLock<Result<Profiler>>> = Lazy::new(|| RwLock::new(Profiler::new())); @@ -158,162 +157,6 @@ impl<'a> Drop for ProfilerGuard<'a> { } } -fn write_thread_name_fallback(current_thread: libc::pthread_t, name: &mut [libc::c_char]) { - let mut len = 0; - let mut base = 1; - - while current_thread as u128 > base && len < MAX_THREAD_NAME { - base *= 10; - len += 1; - } - - let mut index = 0; - while index < len && base > 1 { - base /= 10; - - name[index] = match (48 + (current_thread as u128 / base) % 10).try_into() { - Ok(digit) => digit, - Err(_) => { - log::error!("fail to convert thread_id to string"); - 0 - } - }; - - index += 1; - } -} - -#[cfg(not(all(any(target_os = "linux", target_os = "macos"), target_env = "gnu")))] -fn write_thread_name(current_thread: libc::pthread_t, name: &mut [libc::c_char]) { - write_thread_name_fallback(current_thread, name); -} - -#[cfg(all(any(target_os = "linux", target_os = "macos"), target_env = "gnu"))] -fn write_thread_name(current_thread: libc::pthread_t, name: &mut [libc::c_char]) { - let name_ptr = name as *mut [libc::c_char] as *mut libc::c_char; - let ret = unsafe { libc::pthread_getname_np(current_thread, name_ptr, MAX_THREAD_NAME) }; - - if ret != 0 { - write_thread_name_fallback(current_thread, name); - } -} - -struct ErrnoProtector(libc::c_int); - -impl ErrnoProtector { - fn new() -> Self { - unsafe { - #[cfg(target_os = "linux")] - { - let errno = *libc::__errno_location(); - Self(errno) - } - #[cfg(target_os = "macos")] - { - let errno = *libc::__error(); - Self(errno) - } - } - } -} - -impl Drop for ErrnoProtector { - fn drop(&mut self) { - unsafe { - #[cfg(target_os = "linux")] - { - *libc::__errno_location() = self.0; - } - #[cfg(target_os = "macos")] - { - *libc::__error() = self.0; - } - } - } -} - -#[no_mangle] -#[cfg_attr( - not(all(any(target_arch = "x86_64", target_arch = "aarch64"))), - allow(unused_variables) -)] -extern "C" fn perf_signal_handler( - _signal: c_int, - _siginfo: *mut libc::siginfo_t, - ucontext: *mut libc::c_void, -) { - let _errno = ErrnoProtector::new(); - - if let Some(mut guard) = PROFILER.try_write() { - if let Ok(profiler) = guard.as_mut() { - #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] - if !ucontext.is_null() { - let ucontext: *mut libc::ucontext_t = ucontext as *mut libc::ucontext_t; - - #[cfg(all(target_arch = "x86_64", target_os = "linux"))] - let addr = - unsafe { (*ucontext).uc_mcontext.gregs[libc::REG_RIP as usize] as usize }; - - #[cfg(all(target_arch = "x86_64", target_os = "macos"))] - let addr = unsafe { - let mcontext = (*ucontext).uc_mcontext; - if mcontext.is_null() { - 0 - } else { - (*mcontext).__ss.__rip as usize - } - }; - - #[cfg(all(target_arch = "aarch64", target_os = "linux"))] - let addr = unsafe { (*ucontext).uc_mcontext.pc as usize }; - - #[cfg(all(target_arch = "aarch64", target_os = "macos"))] - let addr = unsafe { - let mcontext = (*ucontext).uc_mcontext; - if mcontext.is_null() { - 0 - } else { - (*mcontext).__ss.__pc as usize - } - }; - - if profiler.is_blocklisted(addr) { - return; - } - } - - let mut bt: SmallVec<[<TraceImpl as Trace>::Frame; MAX_DEPTH]> = - SmallVec::with_capacity(MAX_DEPTH); - let mut index = 0; - - let sample_timestamp: SystemTime = SystemTime::now(); - TraceImpl::trace(ucontext, |frame| { - let ip = Frame::ip(frame); - if profiler.is_blocklisted(ip) { - return false; - } - - if index < MAX_DEPTH { - bt.push(frame.clone()); - index += 1; - true - } else { - false - } - }); - - let current_thread = unsafe { libc::pthread_self() }; - let mut name = [0; MAX_THREAD_NAME]; - let name_ptr = &mut name as *mut [libc::c_char] as *mut libc::c_char; - - write_thread_name(current_thread, &mut name); - - let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) }; - profiler.sample(bt, name.to_bytes(), current_thread as u64, sample_timestamp); - } - } -} - impl Profiler { fn new() -> Result<Self> { Ok(Profiler { @@ -327,7 +170,7 @@ impl Profiler { } #[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64")))] - fn is_blocklisted(&self, addr: usize) -> bool { + pub(crate) fn is_blocklisted(&self, addr: usize) -> bool { for libs in &self.blocklist_segments { if addr > libs.0 && addr < libs.1 { return true; @@ -343,7 +186,7 @@ impl Profiler { if self.running { Err(Error::Running) } else { - self.register_signal_handler()?; + platform::profiler::register()?; self.running = true; Ok(()) @@ -361,7 +204,7 @@ impl Profiler { pub fn stop(&mut self) -> Result<()> { log::info!("stopping cpu profiler"); if self.running { - self.unregister_signal_handler()?; + platform::profiler::unregister()?; self.init()?; Ok(()) @@ -370,25 +213,6 @@ impl Profiler { } } - fn register_signal_handler(&self) -> Result<()> { - let handler = signal::SigHandler::SigAction(perf_signal_handler); - let sigaction = signal::SigAction::new( - handler, - signal::SaFlags::SA_SIGINFO, - signal::SigSet::empty(), - ); - unsafe { signal::sigaction(signal::SIGPROF, &sigaction) }?; - - Ok(()) - } - - fn unregister_signal_handler(&self) -> Result<()> { - let handler = signal::SigHandler::SigIgn; - unsafe { signal::signal(signal::SIGPROF, handler) }?; - - Ok(()) - } - // This function has to be AS-safe pub fn sample( &mut self, diff --git a/src/report.rs b/src/report.rs index a19b940e..a3648a88 100644 --- a/src/report.rs +++ b/src/report.rs @@ -6,8 +6,8 @@ use std::fmt::{Debug, Formatter}; use parking_lot::RwLock; use crate::frames::{Frames, UnresolvedFrames}; +use crate::platform::timer::ReportTiming; use crate::profiler::Profiler; -use crate::timer::ReportTiming; use crate::{Error, Result}; From 017a1164d632ac2309751b079a347072f153f6d8 Mon Sep 17 00:00:00 2001 From: Christian Jordan <svorre2304@gmail.com> Date: Mon, 25 Jul 2022 00:01:44 +0200 Subject: [PATCH 2/5] rename from platform_* to *_impl --- src/platform/mod.rs | 8 +- .../addr_validate.rs | 0 .../{platform_nix => nix_impl}/profiler.rs | 111 +++++++++++++++++- .../{platform_nix => nix_impl}/timer.rs | 0 .../addr_validate.rs | 0 .../profiler.rs | 0 .../timer.rs | 0 7 files changed, 114 insertions(+), 5 deletions(-) rename src/platform/{platform_nix => nix_impl}/addr_validate.rs (100%) rename src/platform/{platform_nix => nix_impl}/profiler.rs (67%) rename src/platform/{platform_nix => nix_impl}/timer.rs (100%) rename src/platform/{platform_windows => windows_impl}/addr_validate.rs (100%) rename src/platform/{platform_windows => windows_impl}/profiler.rs (100%) rename src/platform/{platform_windows => windows_impl}/timer.rs (100%) diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 10cacd48..2db88d9c 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,19 +1,19 @@ #[cfg(any(target_os = "linux", target_os = "macos"))] -mod platform_nix { +mod nix_impl { pub mod addr_validate; pub mod profiler; pub mod timer; } #[cfg(target_os = "windows")] -mod platform_windows { +mod windows_impl { pub mod addr_validate; pub mod profiler; pub mod timer; } #[cfg(any(target_os = "linux", target_os = "macos"))] -pub use platform_nix::*; +pub use nix_impl::*; #[cfg(target_os = "windows")] -pub use platform_windows::*; +pub use windows_impl::*; diff --git a/src/platform/platform_nix/addr_validate.rs b/src/platform/nix_impl/addr_validate.rs similarity index 100% rename from src/platform/platform_nix/addr_validate.rs rename to src/platform/nix_impl/addr_validate.rs diff --git a/src/platform/platform_nix/profiler.rs b/src/platform/nix_impl/profiler.rs similarity index 67% rename from src/platform/platform_nix/profiler.rs rename to src/platform/nix_impl/profiler.rs index 9561193a..4b0015db 100644 --- a/src/platform/platform_nix/profiler.rs +++ b/src/platform/nix_impl/profiler.rs @@ -6,7 +6,7 @@ use smallvec::SmallVec; use nix::sys::signal; -use crate::error::{Error, Result}; +use crate::error::Result; use crate::profiler::PROFILER; use crate::{MAX_DEPTH, MAX_THREAD_NAME}; @@ -183,3 +183,112 @@ extern "C" fn perf_signal_handler( } } } + +#[cfg(test)] +mod tests { + use super::*; + + use std::cell::RefCell; + use std::ffi::c_void; + use std::ptr::null_mut; + + #[cfg(not(target_env = "gnu"))] + #[allow(clippy::wrong_self_convention)] + #[allow(non_upper_case_globals)] + static mut __malloc_hook: Option<extern "C" fn(size: usize) -> *mut c_void> = None; + + extern "C" { + #[cfg(target_env = "gnu")] + static mut __malloc_hook: Option<extern "C" fn(size: usize) -> *mut c_void>; + + fn malloc(size: usize) -> *mut c_void; + } + + thread_local! { + static FLAG: RefCell<bool> = RefCell::new(false); + } + + extern "C" fn malloc_hook(size: usize) -> *mut c_void { + unsafe { + __malloc_hook = None; + } + + FLAG.with(|flag| { + flag.replace(true); + }); + let p = unsafe { malloc(size) }; + + unsafe { + __malloc_hook = Some(malloc_hook); + } + + p + } + + #[inline(never)] + fn is_prime_number(v: usize, prime_numbers: &[usize]) -> bool { + if v < 10000 { + let r = prime_numbers.binary_search(&v); + return r.is_ok(); + } + + for n in prime_numbers { + if v % n == 0 { + return false; + } + } + + true + } + + #[inline(never)] + fn prepare_prime_numbers() -> Vec<usize> { + // bootstrap: Generate a prime table of 0..10000 + let mut prime_number_table: [bool; 10000] = [true; 10000]; + prime_number_table[0] = false; + prime_number_table[1] = false; + for i in 2..10000 { + if prime_number_table[i] { + let mut v = i * 2; + while v < 10000 { + prime_number_table[v] = false; + v += i; + } + } + } + let mut prime_numbers = vec![]; + for (i, item) in prime_number_table.iter().enumerate().skip(2) { + if *item { + prime_numbers.push(i); + } + } + prime_numbers + } + + #[cfg(target_os = "linux")] + #[test] + fn malloc_free() { + trigger_lazy(); + + let prime_numbers = prepare_prime_numbers(); + + let mut _v = 0; + + unsafe { + __malloc_hook = Some(malloc_hook); + } + for i in 2..50000 { + if is_prime_number(i, &prime_numbers) { + _v += 1; + perf_signal_handler(27, null_mut(), null_mut()); + } + } + unsafe { + __malloc_hook = None; + } + + FLAG.with(|flag| { + assert!(!*flag.borrow()); + }); + } +} diff --git a/src/platform/platform_nix/timer.rs b/src/platform/nix_impl/timer.rs similarity index 100% rename from src/platform/platform_nix/timer.rs rename to src/platform/nix_impl/timer.rs diff --git a/src/platform/platform_windows/addr_validate.rs b/src/platform/windows_impl/addr_validate.rs similarity index 100% rename from src/platform/platform_windows/addr_validate.rs rename to src/platform/windows_impl/addr_validate.rs diff --git a/src/platform/platform_windows/profiler.rs b/src/platform/windows_impl/profiler.rs similarity index 100% rename from src/platform/platform_windows/profiler.rs rename to src/platform/windows_impl/profiler.rs diff --git a/src/platform/platform_windows/timer.rs b/src/platform/windows_impl/timer.rs similarity index 100% rename from src/platform/platform_windows/timer.rs rename to src/platform/windows_impl/timer.rs From 33b0180980f6d262a841fdcfa9a6d395cc69c9df Mon Sep 17 00:00:00 2001 From: Christian Jordan <svorre2304@gmail.com> Date: Mon, 25 Jul 2022 00:14:28 +0200 Subject: [PATCH 3/5] remove linux tests from profiler they were moved into nix_impl/profiler in previous commit --- src/profiler.rs | 110 ------------------------------------------------ 1 file changed, 110 deletions(-) diff --git a/src/profiler.rs b/src/profiler.rs index 1023fcd3..1a69ef54 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -227,113 +227,3 @@ impl Profiler { if let Ok(()) = self.data.add(frames, 1) {} } } - -#[cfg(test)] -#[cfg(target_os = "linux")] -mod tests { - use super::*; - - use std::cell::RefCell; - use std::ffi::c_void; - use std::ptr::null_mut; - - #[cfg(not(target_env = "gnu"))] - #[allow(clippy::wrong_self_convention)] - #[allow(non_upper_case_globals)] - static mut __malloc_hook: Option<extern "C" fn(size: usize) -> *mut c_void> = None; - - extern "C" { - #[cfg(target_env = "gnu")] - static mut __malloc_hook: Option<extern "C" fn(size: usize) -> *mut c_void>; - - fn malloc(size: usize) -> *mut c_void; - } - - thread_local! { - static FLAG: RefCell<bool> = RefCell::new(false); - } - - extern "C" fn malloc_hook(size: usize) -> *mut c_void { - unsafe { - __malloc_hook = None; - } - - FLAG.with(|flag| { - flag.replace(true); - }); - let p = unsafe { malloc(size) }; - - unsafe { - __malloc_hook = Some(malloc_hook); - } - - p - } - - #[inline(never)] - fn is_prime_number(v: usize, prime_numbers: &[usize]) -> bool { - if v < 10000 { - let r = prime_numbers.binary_search(&v); - return r.is_ok(); - } - - for n in prime_numbers { - if v % n == 0 { - return false; - } - } - - true - } - - #[inline(never)] - fn prepare_prime_numbers() -> Vec<usize> { - // bootstrap: Generate a prime table of 0..10000 - let mut prime_number_table: [bool; 10000] = [true; 10000]; - prime_number_table[0] = false; - prime_number_table[1] = false; - for i in 2..10000 { - if prime_number_table[i] { - let mut v = i * 2; - while v < 10000 { - prime_number_table[v] = false; - v += i; - } - } - } - let mut prime_numbers = vec![]; - for (i, item) in prime_number_table.iter().enumerate().skip(2) { - if *item { - prime_numbers.push(i); - } - } - prime_numbers - } - - #[cfg(target_os = "linux")] - #[test] - fn malloc_free() { - trigger_lazy(); - - let prime_numbers = prepare_prime_numbers(); - - let mut _v = 0; - - unsafe { - __malloc_hook = Some(malloc_hook); - } - for i in 2..50000 { - if is_prime_number(i, &prime_numbers) { - _v += 1; - perf_signal_handler(27, null_mut(), null_mut()); - } - } - unsafe { - __malloc_hook = None; - } - - FLAG.with(|flag| { - assert!(!*flag.borrow()); - }); - } -} From 601129ab85fec53f4ab544ad789edd8b686b7acc Mon Sep 17 00:00:00 2001 From: Christian Jordan <svorre2304@gmail.com> Date: Mon, 25 Jul 2022 00:15:03 +0200 Subject: [PATCH 4/5] move platform specific backtrace into ./platform --- src/{backtrace/mod.rs => backtrace.rs} | 24 ++------------- src/{backtrace => platform}/backtrace_rs.rs | 4 +-- src/platform/mod.rs | 30 +++++++++++++++++++ .../nix_impl}/frame_pointer.rs | 6 ++-- 4 files changed, 37 insertions(+), 27 deletions(-) rename src/{backtrace/mod.rs => backtrace.rs} (65%) rename src/{backtrace => platform}/backtrace_rs.rs (85%) rename src/{backtrace => platform/nix_impl}/frame_pointer.rs (97%) diff --git a/src/backtrace/mod.rs b/src/backtrace.rs similarity index 65% rename from src/backtrace/mod.rs rename to src/backtrace.rs index 44cd3214..59a569b7 100644 --- a/src/backtrace/mod.rs +++ b/src/backtrace.rs @@ -3,6 +3,8 @@ use libc::c_void; use std::path::PathBuf; +pub use crate::platform::TraceImpl; + pub trait Symbol: Sized { fn name(&self) -> Option<Vec<u8>>; fn addr(&self) -> Option<*mut c_void>; @@ -43,25 +45,3 @@ pub trait Trace { where Self: Sized; } - -#[cfg(not(all( - any(target_arch = "x86_64", target_arch = "aarch64"), - feature = "frame-pointer" -)))] -mod backtrace_rs; -#[cfg(not(all( - any(target_arch = "x86_64", target_arch = "aarch64"), - feature = "frame-pointer" -)))] -pub use backtrace_rs::Trace as TraceImpl; - -#[cfg(all( - any(target_arch = "x86_64", target_arch = "aarch64"), - feature = "frame-pointer" -))] -pub mod frame_pointer; -#[cfg(all( - any(target_arch = "x86_64", target_arch = "aarch64"), - feature = "frame-pointer" -))] -pub use frame_pointer::Trace as TraceImpl; diff --git a/src/backtrace/backtrace_rs.rs b/src/platform/backtrace_rs.rs similarity index 85% rename from src/backtrace/backtrace_rs.rs rename to src/platform/backtrace_rs.rs index 1b7e3663..b2f18049 100644 --- a/src/backtrace/backtrace_rs.rs +++ b/src/platform/backtrace_rs.rs @@ -1,4 +1,4 @@ -impl super::Frame for backtrace::Frame { +impl crate::backtrace::Frame for backtrace::Frame { type S = backtrace::Symbol; fn ip(&self) -> usize { @@ -16,7 +16,7 @@ impl super::Frame for backtrace::Frame { pub struct Trace {} -impl super::Trace for Trace { +impl crate::backtrace::Trace for Trace { type Frame = backtrace::Frame; fn trace<F: FnMut(&Self::Frame) -> bool>(_: *mut libc::c_void, cb: F) { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 2db88d9c..effbefad 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -3,6 +3,29 @@ mod nix_impl { pub mod addr_validate; pub mod profiler; pub mod timer; + + #[cfg(all( + any(target_arch = "x86_64", target_arch = "aarch64"), + feature = "frame-pointer", + ))] + mod frame_pointer; + #[cfg(all( + any(target_arch = "x86_64", target_arch = "aarch64"), + feature = "frame-pointer", + ))] + pub use frame_pointer::Trace as TraceImpl; + + #[cfg(not(all( + any(target_arch = "x86_64", target_arch = "aarch64"), + feature = "frame-pointer", + )))] + #[path = "../backtrace_rs.rs"] + mod backtrace_rs; + #[cfg(not(all( + any(target_arch = "x86_64", target_arch = "aarch64"), + feature = "frame-pointer", + )))] + pub use backtrace_rs::Trace as TraceImpl; } #[cfg(target_os = "windows")] @@ -10,6 +33,13 @@ mod windows_impl { pub mod addr_validate; pub mod profiler; pub mod timer; + + #[cfg(feature = "frame-pointer")] + std::compile_error!("frame-pointer feature is currently not supported on windows."); + + #[path = "../backtrace_rs.rs"] + mod backtrace_rs; + pub use backtrace_rs::Trace as TraceImpl; } #[cfg(any(target_os = "linux", target_os = "macos"))] diff --git a/src/backtrace/frame_pointer.rs b/src/platform/nix_impl/frame_pointer.rs similarity index 97% rename from src/backtrace/frame_pointer.rs rename to src/platform/nix_impl/frame_pointer.rs index 1810e4a0..f95bc4c8 100644 --- a/src/backtrace/frame_pointer.rs +++ b/src/platform/nix_impl/frame_pointer.rs @@ -4,7 +4,7 @@ use std::ptr::null_mut; use libc::c_void; -use crate::addr_validate::validate; +use crate::validate; #[derive(Clone, Debug)] pub struct Frame { @@ -16,7 +16,7 @@ extern "C" { } -impl super::Frame for Frame { +impl crate::backtrace::Frame for Frame { type S = backtrace::Symbol; fn ip(&self) -> usize { @@ -37,7 +37,7 @@ impl super::Frame for Frame { } pub struct Trace {} -impl super::Trace for Trace { +impl crate::backtrace::Trace for Trace { type Frame = Frame; fn trace<F: FnMut(&Self::Frame) -> bool>(ucontext: *mut libc::c_void, mut cb: F) { From ced8cdabf37e7b5bd29c5fda837a764c3c16f72e Mon Sep 17 00:00:00 2001 From: Christian Jordan <svorre2304@gmail.com> Date: Mon, 25 Jul 2022 00:32:50 +0200 Subject: [PATCH 5/5] Use fmt::write instead of push_str To make clippy happy the .unwrap on write! shouldn't fail --- src/report.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/report.rs b/src/report.rs index a3648a88..39863538 100644 --- a/src/report.rs +++ b/src/report.rs @@ -159,7 +159,7 @@ impl Debug for Report { mod flamegraph { use super::*; use inferno::flamegraph; - use std::io::Write; + use std::{fmt::Write as _, io::Write}; impl Report { /// `flamegraph` will write an svg flamegraph into `writer` **only available with `flamegraph` feature** @@ -188,13 +188,13 @@ mod flamegraph { for frame in key.frames.iter().rev() { for symbol in frame.iter().rev() { - line.push_str(&format!("{}", symbol)); + write!(line, "{}", symbol).unwrap(); line.push(';'); } } line.pop().unwrap_or_default(); - line.push_str(&format!(" {}", value)); + write!(line, " {}", value).unwrap(); line })