use crate::{
object_get_info_single, ok, sys, AsHandleRef, ExceptionReport, Handle, HandleBased, HandleRef,
MonotonicDuration, ObjectQuery, Profile, Status, Task, Topic,
};
use bitflags::bitflags;
#[cfg(target_arch = "x86_64")]
use crate::{object_set_property, Property, PropertyQuery};
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RaiseExceptionOptions: u32 {
const TARGET_JOB_DEBUGGER = sys::ZX_EXCEPTION_TARGET_JOB_DEBUGGER;
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Thread(Handle);
impl_handle_based!(Thread);
struct ThreadExceptionReport;
unsafe impl ObjectQuery for ThreadExceptionReport {
const TOPIC: Topic = Topic::THREAD_EXCEPTION_REPORT;
type InfoTy = sys::zx_exception_report_t;
}
impl Thread {
pub fn start(
&self,
thread_entry: usize,
stack: usize,
arg1: usize,
arg2: usize,
) -> Result<(), Status> {
let thread_raw = self.raw_handle();
let status = unsafe { sys::zx_thread_start(thread_raw, thread_entry, stack, arg1, arg2) };
ok(status)
}
pub fn set_profile(&self, profile: Profile, options: u32) -> Result<(), Status> {
let thread_raw = self.raw_handle();
let profile_raw = profile.raw_handle();
let status = unsafe { sys::zx_object_set_profile(thread_raw, profile_raw, options) };
ok(status)
}
pub unsafe fn exit() {
sys::zx_thread_exit()
}
pub fn get_exception_report(&self) -> Result<ExceptionReport, Status> {
let raw = object_get_info_single::<ThreadExceptionReport>(self.as_handle_ref())?;
Ok(unsafe { ExceptionReport::from_raw(raw) })
}
pub fn get_thread_info(&self) -> Result<ThreadInfo, Status> {
Ok(ThreadInfo::from_raw(object_get_info_single::<ThreadInfoQuery>(self.as_handle_ref())?))
}
pub fn get_stats(&self) -> Result<ThreadStats, Status> {
Ok(ThreadStats::from_raw(object_get_info_single::<ThreadStatsQuery>(self.as_handle_ref())?))
}
pub fn read_state_general_regs(&self) -> Result<sys::zx_thread_state_general_regs_t, Status> {
let mut state = sys::zx_thread_state_general_regs_t::default();
let thread_raw = self.raw_handle();
let status = unsafe {
sys::zx_thread_read_state(
thread_raw,
sys::ZX_THREAD_STATE_GENERAL_REGS,
std::ptr::from_mut(&mut state).cast::<u8>(),
std::mem::size_of_val(&state),
)
};
ok(status).map(|_| state)
}
pub fn write_state_general_regs(
&self,
state: sys::zx_thread_state_general_regs_t,
) -> Result<(), Status> {
let thread_raw = self.raw_handle();
let status = unsafe {
sys::zx_thread_write_state(
thread_raw,
sys::ZX_THREAD_STATE_GENERAL_REGS,
std::ptr::from_ref(&state).cast::<u8>(),
std::mem::size_of_val(&state),
)
};
ok(status)
}
pub fn raise_user_exception(
options: RaiseExceptionOptions,
synth_code: u32,
synth_data: u32,
) -> Result<(), Status> {
let arch = unsafe { std::mem::zeroed::<sys::zx_exception_header_arch_t>() };
let context = sys::zx_exception_context_t { arch, synth_code, synth_data };
ok(unsafe { sys::zx_thread_raise_exception(options.bits(), sys::ZX_EXCP_USER, &context) })
}
}
impl Task for Thread {}
struct ThreadStatsQuery;
unsafe impl ObjectQuery for ThreadStatsQuery {
const TOPIC: Topic = Topic::THREAD_STATS;
type InfoTy = sys::zx_info_thread_stats_t;
}
#[cfg(target_arch = "x86_64")]
unsafe_handle_properties!(object: Thread,
props: [
{query_ty: REGISTER_GS, tag: RegisterGsTag, prop_ty: usize, set: set_register_gs},
{query_ty: REGISTER_FS, tag: RegisterFsTag, prop_ty: usize, set: set_register_fs},
]
);
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct ThreadStats {
pub total_runtime: MonotonicDuration,
pub last_scheduled_cpu: u32,
}
impl ThreadStats {
fn from_raw(
sys::zx_info_thread_stats_t { total_runtime, last_scheduled_cpu }: sys::zx_info_thread_stats_t,
) -> Self {
Self { total_runtime: MonotonicDuration::from_nanos(total_runtime), last_scheduled_cpu }
}
}
struct ThreadInfoQuery;
unsafe impl ObjectQuery for ThreadInfoQuery {
const TOPIC: Topic = Topic::THREAD;
type InfoTy = sys::zx_info_thread_t;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ThreadInfo {
pub state: ThreadState,
pub cpu_affinity_mask: sys::zx_cpu_set_t,
}
impl ThreadInfo {
fn from_raw(raw: sys::zx_info_thread_t) -> Self {
Self {
state: ThreadState::from_raw(raw.state, raw.wait_exception_channel_type),
cpu_affinity_mask: raw.cpu_affinity_mask,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ThreadState {
New,
Running,
Suspended,
Blocked(ThreadBlockType),
Dying,
Dead,
Unknown(u32),
}
impl ThreadState {
fn from_raw(raw_state: u32, raw_wait_exception_channel_type: u32) -> Self {
match raw_state {
sys::ZX_THREAD_STATE_NEW => Self::New,
sys::ZX_THREAD_STATE_RUNNING => Self::Running,
sys::ZX_THREAD_STATE_SUSPENDED => Self::Suspended,
sys::ZX_THREAD_STATE_BLOCKED => Self::Blocked(ThreadBlockType::Unknown),
sys::ZX_THREAD_STATE_BLOCKED_EXCEPTION => Self::Blocked(ThreadBlockType::Exception(
ExceptionChannelType::from_raw(raw_wait_exception_channel_type),
)),
sys::ZX_THREAD_STATE_BLOCKED_SLEEPING => Self::Blocked(ThreadBlockType::Sleeping),
sys::ZX_THREAD_STATE_BLOCKED_FUTEX => Self::Blocked(ThreadBlockType::Futex),
sys::ZX_THREAD_STATE_BLOCKED_PORT => Self::Blocked(ThreadBlockType::Port),
sys::ZX_THREAD_STATE_BLOCKED_CHANNEL => Self::Blocked(ThreadBlockType::Channel),
sys::ZX_THREAD_STATE_BLOCKED_WAIT_ONE => Self::Blocked(ThreadBlockType::WaitOne),
sys::ZX_THREAD_STATE_BLOCKED_WAIT_MANY => Self::Blocked(ThreadBlockType::WaitMany),
sys::ZX_THREAD_STATE_BLOCKED_INTERRUPT => Self::Blocked(ThreadBlockType::Interrupt),
sys::ZX_THREAD_STATE_BLOCKED_PAGER => Self::Blocked(ThreadBlockType::Pager),
sys::ZX_THREAD_STATE_DYING => Self::Dying,
sys::ZX_THREAD_STATE_DEAD => Self::Dead,
_ => Self::Unknown(raw_state),
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ThreadBlockType {
Exception(ExceptionChannelType),
Sleeping,
Futex,
Port,
Channel,
WaitOne,
WaitMany,
Interrupt,
Pager,
Unknown,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ExceptionChannelType {
None,
Debugger,
Thread,
Process,
Job,
JobDebugger,
Unknown(u32),
}
impl ExceptionChannelType {
fn from_raw(raw_wait_exception_channel_type: u32) -> Self {
match raw_wait_exception_channel_type {
sys::ZX_EXCEPTION_CHANNEL_TYPE_NONE => Self::None,
sys::ZX_EXCEPTION_CHANNEL_TYPE_DEBUGGER => Self::Debugger,
sys::ZX_EXCEPTION_CHANNEL_TYPE_THREAD => Self::Thread,
sys::ZX_EXCEPTION_CHANNEL_TYPE_PROCESS => Self::Process,
sys::ZX_EXCEPTION_CHANNEL_TYPE_JOB => Self::Job,
sys::ZX_EXCEPTION_CHANNEL_TYPE_JOB_DEBUGGER => Self::JobDebugger,
_ => Self::Unknown(raw_wait_exception_channel_type),
}
}
}
#[cfg(test)]
mod tests {
use zx::{Handle, Profile, Status};
#[test]
fn set_profile_invalid() {
let thread = fuchsia_runtime::thread_self();
let profile = Profile::from(Handle::invalid());
assert_eq!(thread.set_profile(profile, 0), Err(Status::BAD_HANDLE));
}
}