diff options
author | Josh Matthews <josh@joshmatthews.net> | 2019-03-25 17:07:32 -0400 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2019-03-29 23:05:44 -0400 |
commit | 7a6fb65bd9fa8097ccecb2d522a20c2e2ecf0055 (patch) | |
tree | f837db42efb5ed03a2082193478ed880e1cededc /components/background_hang_monitor | |
parent | 7bc29205ef6b957245deff0232cf9d0d5a1fe846 (diff) | |
download | servo-7a6fb65bd9fa8097ccecb2d522a20c2e2ecf0055.tar.gz servo-7a6fb65bd9fa8097ccecb2d522a20c2e2ecf0055.zip |
Use libunwind to walk the stack of the paused thread.
Diffstat (limited to 'components/background_hang_monitor')
-rw-r--r-- | components/background_hang_monitor/sampler_linux.rs | 83 |
1 files changed, 75 insertions, 8 deletions
diff --git a/components/background_hang_monitor/sampler_linux.rs b/components/background_hang_monitor/sampler_linux.rs index 308b7f7f4b9..e57e2ea9fc7 100644 --- a/components/background_hang_monitor/sampler_linux.rs +++ b/components/background_hang_monitor/sampler_linux.rs @@ -11,13 +11,18 @@ use std::cell::UnsafeCell; use std::io; use std::mem; use std::process; +use std::ptr; +use std::sync::atomic::{AtomicPtr, Ordering}; use std::thread; +use unwind_sys::{ + unw_cursor_t, unw_get_reg, unw_init_local, unw_step, UNW_ESUCCESS, UNW_REG_IP, UNW_REG_SP, +}; static mut SHARED_STATE: SharedState = SharedState { msg2: None, msg3: None, msg4: None, - context: None, + context: AtomicPtr::new(ptr::null_mut()), }; type MonitoredThreadId = libc::pid_t; @@ -27,7 +32,7 @@ struct SharedState { msg2: Option<PosixSemaphore>, msg3: Option<PosixSemaphore>, msg4: Option<PosixSemaphore>, - context: Option<libc::ucontext_t>, + context: AtomicPtr<libc::ucontext_t>, } fn clear_shared_state() { @@ -35,7 +40,7 @@ fn clear_shared_state() { SHARED_STATE.msg2 = None; SHARED_STATE.msg3 = None; SHARED_STATE.msg4 = None; - SHARED_STATE.context = None; + SHARED_STATE.context = AtomicPtr::new(ptr::null_mut()); } } @@ -44,7 +49,7 @@ fn reset_shared_state() { SHARED_STATE.msg2 = Some(PosixSemaphore::new(0).expect("valid semaphore")); SHARED_STATE.msg3 = Some(PosixSemaphore::new(0).expect("valid semaphore")); SHARED_STATE.msg4 = Some(PosixSemaphore::new(0).expect("valid semaphore")); - SHARED_STATE.context = None; + SHARED_STATE.context = AtomicPtr::new(ptr::null_mut()); } } @@ -135,6 +140,42 @@ impl LinuxSampler { } } +enum RegNum { + Ip = UNW_REG_IP as isize, + Sp = UNW_REG_SP as isize, +} + +fn get_register(cursor: &mut unw_cursor_t, num: RegNum) -> Result<u64, i32> { + unsafe { + let mut val = 0; + let ret = unw_get_reg(cursor, num as i32, &mut val); + if ret == UNW_ESUCCESS { + Ok(val) + } else { + Err(ret) + } + } +} + +fn step(cursor: &mut unw_cursor_t) -> Result<bool, i32> { + unsafe { + // libunwind 1.1 seems to get confused and walks off the end of the stack. The last IP + // it reports is 0, so we'll stop if we're there. + if get_register(cursor, RegNum::Ip).unwrap_or(1) == 0 { + return Ok(false); + } + + let ret = unw_step(cursor); + if ret > 0 { + Ok(true) + } else if ret == 0 { + Ok(false) + } else { + Err(ret) + } + } +} + impl Sampler for LinuxSampler { #[allow(unsafe_code)] fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()> { @@ -157,7 +198,32 @@ impl Sampler for LinuxSampler { .expect("msg2 failed"); } - //let results = unsafe { callback(&mut SHARED_STATE.context.expect("valid context")) }; + let context = unsafe { SHARED_STATE.context.load(Ordering::SeqCst) }; + let mut cursor = unsafe { mem::uninitialized() }; + let ret = unsafe { unw_init_local(&mut cursor, context) }; + let result = if ret == UNW_ESUCCESS { + let mut native_stack = NativeStack::new(); + loop { + let ip = match get_register(&mut cursor, RegNum::Ip) { + Ok(ip) => ip, + Err(_) => break, + }; + let sp = match get_register(&mut cursor, RegNum::Sp) { + Ok(sp) => sp, + Err(_) => break, + }; + if native_stack + .process_register(ip as *mut _, sp as *mut _) + .is_err() || + !step(&mut cursor).unwrap_or(false) + { + break; + } + } + Ok(native_stack) + } else { + Err(()) + }; // signal the thread to continue. unsafe { @@ -182,7 +248,7 @@ impl Sampler for LinuxSampler { clear_shared_state(); // NOTE: End of "critical section". - Err(()) + result } } @@ -202,8 +268,9 @@ extern "C" fn sigprof_handler( assert_eq!(sig, libc::SIGPROF); unsafe { // copy the context. - let context: libc::ucontext_t = *(ctx as *mut libc::ucontext_t); - SHARED_STATE.context = Some(context); + SHARED_STATE + .context + .store(ctx as *mut libc::ucontext_t, Ordering::SeqCst); // Tell the sampler we copied the context. SHARED_STATE.msg2.as_ref().unwrap().post().expect("posted"); |