mirror of
https://github.com/servo/servo.git
synced 2025-08-07 22:45:34 +01:00
introduce a background-hang-monitor:
Mac-Os implementation of a thread sampler, Linux and Windows skeleton implementations.
This commit is contained in:
parent
7c65505df3
commit
4eb785cdc0
23 changed files with 1134 additions and 11 deletions
118
components/background_hang_monitor/sampler_mac.rs
Normal file
118
components/background_hang_monitor/sampler_mac.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::sampler::{Address, NativeStack, Registers, Sampler};
|
||||
use libc;
|
||||
use mach;
|
||||
use std::panic;
|
||||
use std::process;
|
||||
|
||||
type MonitoredThreadId = mach::mach_types::thread_act_t;
|
||||
|
||||
pub struct MacOsSampler {
|
||||
thread_id: MonitoredThreadId,
|
||||
}
|
||||
|
||||
impl MacOsSampler {
|
||||
#[allow(unsafe_code)]
|
||||
pub fn new() -> Box<Sampler> {
|
||||
let thread_id = unsafe { mach::mach_init::mach_thread_self() };
|
||||
Box::new(MacOsSampler { thread_id })
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler for MacOsSampler {
|
||||
#[allow(unsafe_code)]
|
||||
fn suspend_and_sample_thread(&self) -> Result<NativeStack, ()> {
|
||||
// Warning: The "critical section" begins here.
|
||||
// In the critical section:
|
||||
// we must not do any dynamic memory allocation,
|
||||
// nor try to acquire any lock
|
||||
// or any other unshareable resource.
|
||||
let current_hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|_| {
|
||||
// Avoiding any allocation or locking as part of standard panicking.
|
||||
process::abort();
|
||||
}));
|
||||
let native_stack = unsafe {
|
||||
if let Err(()) = suspend_thread(self.thread_id) {
|
||||
panic::set_hook(current_hook);
|
||||
return Err(());
|
||||
};
|
||||
let native_stack = match get_registers(self.thread_id) {
|
||||
Ok(regs) => Ok(frame_pointer_stack_walk(regs)),
|
||||
Err(()) => Err(()),
|
||||
};
|
||||
if let Err(()) = resume_thread(self.thread_id) {
|
||||
process::abort();
|
||||
}
|
||||
native_stack
|
||||
};
|
||||
panic::set_hook(current_hook);
|
||||
// NOTE: End of "critical section".
|
||||
native_stack
|
||||
}
|
||||
}
|
||||
|
||||
fn check_kern_return(kret: mach::kern_return::kern_return_t) -> Result<(), ()> {
|
||||
if kret != mach::kern_return::KERN_SUCCESS {
|
||||
return Err(());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn suspend_thread(thread_id: MonitoredThreadId) -> Result<(), ()> {
|
||||
check_kern_return(mach::thread_act::thread_suspend(thread_id))
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn get_registers(thread_id: MonitoredThreadId) -> Result<Registers, ()> {
|
||||
let mut state = mach::structs::x86_thread_state64_t::new();
|
||||
let mut state_count = mach::structs::x86_thread_state64_t::count();
|
||||
let kret = mach::thread_act::thread_get_state(
|
||||
thread_id,
|
||||
mach::thread_status::x86_THREAD_STATE64,
|
||||
(&mut state) as *mut _ as *mut _,
|
||||
&mut state_count,
|
||||
);
|
||||
check_kern_return(kret)?;
|
||||
Ok(Registers {
|
||||
instruction_ptr: state.__rip as Address,
|
||||
stack_ptr: state.__rsp as Address,
|
||||
frame_ptr: state.__rbp as Address,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn resume_thread(thread_id: MonitoredThreadId) -> Result<(), ()> {
|
||||
check_kern_return(mach::thread_act::thread_resume(thread_id))
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn frame_pointer_stack_walk(regs: Registers) -> NativeStack {
|
||||
// Note: this function will only work with code build with:
|
||||
// --dev,
|
||||
// or --with-frame-pointer.
|
||||
|
||||
let stackaddr = libc::pthread_get_stackaddr_np(libc::pthread_self());
|
||||
let mut native_stack = NativeStack::new();
|
||||
let pc = regs.instruction_ptr as *mut std::ffi::c_void;
|
||||
let stack = regs.stack_ptr as *mut std::ffi::c_void;
|
||||
let _ = native_stack.process_register(pc, stack);
|
||||
let mut current = regs.frame_ptr as *mut *mut std::ffi::c_void;
|
||||
while !current.is_null() {
|
||||
if (current as usize) < stackaddr as usize {
|
||||
break;
|
||||
}
|
||||
let next = *current as *mut *mut std::ffi::c_void;
|
||||
let pc = current.add(1);
|
||||
let stack = current.add(2);
|
||||
if let Err(()) = native_stack.process_register(*pc, *stack) {
|
||||
break;
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
native_stack
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue