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 {
pub(crate) crash_context: Option<crash_context::CrashContext>,
pub(crate) memory_blocks: Vec<MDMemoryDescriptor>,
pub(crate) task: task_t,
pub(crate) handler_thread: thread_t,
}
impl MinidumpWriter {
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(|| {
unsafe { mach2::traps::mach_task_self() }
}),
handler_thread: handler_thread.unwrap_or_else(|| {
unsafe { mach2::mach_init::mach_thread_self() }
}),
}
}
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,
}
}
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)),
];
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,
time_date_stamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() as u32, flags: 0,
};
header_section.set_value(&mut buffer, header)?;
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())
}
#[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
}
}