diff --git a/Cargo.toml b/Cargo.toml index 45b54b57..b4cc8b37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ protobuf-codec = ["protobuf", "protobuf-codegen-pure", "_protobuf"] cpp = ["symbolic-demangle/cpp"] [dependencies] -backtrace = "0.3" +backtrace = { git = "https://github.com/Jardynq/backtrace-rs" } once_cell = "1.9" libc = "^0.2.66" log = "0.4" @@ -37,6 +37,15 @@ prost-derive = { version = "0.10", optional = true } protobuf = { version = "2.0", optional = true } criterion = {version = "0.3", optional = true} +[target.'cfg(windows)'.dependencies] +winproc = "0.6.4" +winapi = {version = "0.3", features = [ + "processthreadsapi", + "winnt", + "errhandlingapi", + "winuser" +] } + [dependencies.symbolic-demangle] version = "8.0" default-features = false diff --git a/src/error.rs b/src/error.rs index 6d5747ea..5f759594 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,8 +2,8 @@ #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("{0}")] - NixError(#[from] nix::Error), + //#[error("{0}")] + //OsError(#[from] nix::Error), #[error("{0}")] IoError(#[from] std::io::Error), #[error("create profiler error")] diff --git a/src/profiler.rs b/src/profiler.rs index d535e942..e60d0a57 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -4,7 +4,7 @@ use std::convert::TryInto; use std::os::raw::c_int; use backtrace::Frame; -use nix::sys::signal; +//use nix::sys::signal; use once_cell::sync::Lazy; use parking_lot::RwLock; use smallvec::SmallVec; @@ -156,117 +156,41 @@ 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); - } -} +fn perf_worker_thread() { + let worker_id = unsafe { winapi::um::processthreadsapi::GetCurrentThreadId() }; + loop { + let proc = winproc::Process::current(); + let threads = proc.thread_ids().unwrap(); -#[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, -) { - if let Some(mut guard) = PROFILER.try_write() { - if let Ok(profiler) = guard.as_mut() { - #[cfg(all(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 + if let Some(mut guard) = PROFILER.try_write() { + if let Ok(profiler) = guard.as_mut() { + for id in threads { + if id == worker_id { + continue; } - }; - #[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 + let mut bt: SmallVec<[Frame; MAX_DEPTH]> = SmallVec::with_capacity(MAX_DEPTH); + let mut index = 0; + + unsafe { + const ALL_ACCESS: u32 = 2_097_151u32; + let handle = winapi::um::processthreadsapi::OpenThread(ALL_ACCESS, 0, id) as usize; + + backtrace::trace_thread_unsynchronized(handle as _, |frame| { + if index < MAX_DEPTH { + bt.push(frame.clone()); + index += 1; + true + } else { + false + } + }); } - }; - - if profiler.is_blocklisted(addr) { - return; + + profiler.sample(bt, format!("{:x}", id).as_bytes(), id as u64); } } - - let mut bt: SmallVec<[Frame; MAX_DEPTH]> = SmallVec::with_capacity(MAX_DEPTH); - let mut index = 0; - - unsafe { - backtrace::trace_unsynchronized(|frame| { - 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); } } } @@ -328,21 +252,11 @@ 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) }?; - + std::thread::spawn(perf_worker_thread); Ok(()) } fn unregister_signal_handler(&self) -> Result<()> { - let handler = signal::SigHandler::SigIgn; - unsafe { signal::signal(signal::SIGPROF, handler) }?; - Ok(()) } diff --git a/src/timer.rs b/src/timer.rs index a6bfa1fe..58fff200 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -18,9 +18,7 @@ struct Itimerval { pub it_value: Timeval, } -extern "C" { - fn setitimer(which: c_int, new_value: *mut Itimerval, old_value: *mut Itimerval) -> c_int; -} + const ITIMER_PROF: c_int = 2; @@ -40,14 +38,7 @@ impl Timer { let it_value = it_interval.clone(); unsafe { - setitimer( - ITIMER_PROF, - &mut Itimerval { - it_interval, - it_value, - }, - null_mut(), - ) + }; Timer { @@ -76,14 +67,7 @@ impl Drop for Timer { }; let it_value = it_interval.clone(); unsafe { - setitimer( - ITIMER_PROF, - &mut Itimerval { - it_interval, - it_value, - }, - null_mut(), - ) + }; } }