diff options
Diffstat (limited to 'third_party/rust/minidump-writer/src/mac/minidump_writer.rs')
-rw-r--r-- | third_party/rust/minidump-writer/src/mac/minidump_writer.rs | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/third_party/rust/minidump-writer/src/mac/minidump_writer.rs b/third_party/rust/minidump-writer/src/mac/minidump_writer.rs new file mode 100644 index 0000000000..b05662fd21 --- /dev/null +++ b/third_party/rust/minidump-writer/src/mac/minidump_writer.rs @@ -0,0 +1,187 @@ +use crate::{ + dir_section::{DirSection, DumpBuf}, + mac::{errors::WriterError, task_dumper::TaskDumper}, + mem_writer::*, + minidump_format::{self, MDMemoryDescriptor, MDRawDirectory, MDRawHeader}, +}; +use std::io::{Seek, Write}; + +pub use mach2::mach_types::{task_t, thread_t}; + +type Result<T> = std::result::Result<T, WriterError>; + +pub struct MinidumpWriter { + /// The crash context as captured by an exception handler + pub(crate) crash_context: Option<crash_context::CrashContext>, + /// List of raw blocks of memory we've written into the stream. These are + /// referenced by other streams (eg thread list) + pub(crate) memory_blocks: Vec<MDMemoryDescriptor>, + /// The task being dumped + pub(crate) task: task_t, + /// The handler thread, so it can be ignored/deprioritized + pub(crate) handler_thread: thread_t, +} + +impl MinidumpWriter { + /// Creates a minidump writer for the specified mach task (process) and + /// handler thread. If not specified, defaults to the current task and thread. + /// + /// ``` + /// use minidump_writer::{minidump_writer::MinidumpWriter, mach2}; + /// + /// // Note that this is the same as specifying `None` for both the task and + /// // handler thread, this is just meant to illustrate how you can setup + /// // a MinidumpWriter manually instead of using a `CrashContext` + /// // SAFETY: syscalls + /// let mdw = unsafe { + /// MinidumpWriter::new( + /// Some(mach2::traps::mach_task_self()), + /// Some(mach2::mach_init::mach_thread_self()), + /// ) + /// }; + /// ``` + pub fn new(task: Option<task_t>, handler_thread: Option<thread_t>) -> Self { + Self { + crash_context: None, + memory_blocks: Vec::new(), + task: task.unwrap_or_else(|| { + // SAFETY: syscall + unsafe { mach2::traps::mach_task_self() } + }), + handler_thread: handler_thread.unwrap_or_else(|| { + // SAFETY: syscall + unsafe { mach2::mach_init::mach_thread_self() } + }), + } + } + + /// Creates a minidump writer with the specified crash context, presumably + /// for another task + pub fn with_crash_context(crash_context: crash_context::CrashContext) -> Self { + let task = crash_context.task; + let handler_thread = crash_context.handler_thread; + + Self { + crash_context: Some(crash_context), + memory_blocks: Vec::new(), + task, + handler_thread, + } + } + + /// Writes a minidump to the specified destination, returning the raw minidump + /// contents upon success + pub fn dump(&mut self, destination: &mut (impl Write + Seek)) -> Result<Vec<u8>> { + let writers = { + #[allow(clippy::type_complexity)] + let mut writers: Vec< + Box<dyn FnMut(&mut Self, &mut DumpBuf, &TaskDumper) -> Result<MDRawDirectory>>, + > = vec![ + Box::new(|mw, buffer, dumper| mw.write_thread_list(buffer, dumper)), + Box::new(|mw, buffer, dumper| mw.write_memory_list(buffer, dumper)), + Box::new(|mw, buffer, dumper| mw.write_system_info(buffer, dumper)), + Box::new(|mw, buffer, dumper| mw.write_module_list(buffer, dumper)), + Box::new(|mw, buffer, dumper| mw.write_misc_info(buffer, dumper)), + Box::new(|mw, buffer, dumper| mw.write_breakpad_info(buffer, dumper)), + Box::new(|mw, buffer, dumper| mw.write_thread_names(buffer, dumper)), + ]; + + // Exception stream needs to be the last entry in this array as it may + // be omitted in the case where the minidump is written without an + // exception. + if self + .crash_context + .as_ref() + .and_then(|cc| cc.exception.as_ref()) + .is_some() + { + writers.push(Box::new(|mw, buffer, dumper| { + mw.write_exception(buffer, dumper) + })); + } + + writers + }; + + let num_writers = writers.len() as u32; + let mut buffer = Buffer::with_capacity(0); + + let mut header_section = MemoryWriter::<MDRawHeader>::alloc(&mut buffer)?; + let mut dir_section = DirSection::new(&mut buffer, num_writers, destination)?; + + let header = MDRawHeader { + signature: minidump_format::MD_HEADER_SIGNATURE, + version: minidump_format::MD_HEADER_VERSION, + stream_count: num_writers, + stream_directory_rva: dir_section.position(), + checksum: 0, /* Can be 0. In fact, that's all that's + * been found in minidump files. */ + time_date_stamp: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as u32, // TODO: This is not Y2038 safe, but thats how its currently defined as + flags: 0, + }; + header_section.set_value(&mut buffer, header)?; + + // Ensure the header gets flushed. If we crash somewhere below, + // we should have a mostly-intact dump + dir_section.write_to_file(&mut buffer, None)?; + + let dumper = super::task_dumper::TaskDumper::new(self.task); + + for mut writer in writers { + let dirent = writer(self, &mut buffer, &dumper)?; + dir_section.write_to_file(&mut buffer, Some(dirent))?; + } + + Ok(buffer.into()) + } + + /// Retrieves the list of active threads in the target process, except + /// the handler thread if it is known, to simplify dump analysis + #[inline] + pub(crate) fn threads(&self, dumper: &TaskDumper) -> ActiveThreads { + ActiveThreads { + threads: dumper.read_threads().unwrap_or_default(), + handler_thread: self.handler_thread, + i: 0, + } + } +} + +pub(crate) struct ActiveThreads { + threads: &'static [u32], + handler_thread: u32, + i: usize, +} + +impl ActiveThreads { + #[inline] + pub(crate) fn len(&self) -> usize { + let mut len = self.threads.len(); + + if self.handler_thread != mach2::port::MACH_PORT_NULL { + len -= 1; + } + + len + } +} + +impl Iterator for ActiveThreads { + type Item = u32; + + fn next(&mut self) -> Option<Self::Item> { + while self.i < self.threads.len() { + let i = self.i; + self.i += 1; + + if self.threads[i] != self.handler_thread { + return Some(self.threads[i]); + } + } + + None + } +} |