diff options
Diffstat (limited to 'third_party/rust/minidump_writer_linux/src/sections')
7 files changed, 688 insertions, 0 deletions
diff --git a/third_party/rust/minidump_writer_linux/src/sections/app_memory.rs b/third_party/rust/minidump_writer_linux/src/sections/app_memory.rs new file mode 100644 index 0000000000..93644119cf --- /dev/null +++ b/third_party/rust/minidump_writer_linux/src/sections/app_memory.rs @@ -0,0 +1,25 @@ +use crate::linux_ptrace_dumper::LinuxPtraceDumper; +use crate::minidump_format::*; +use crate::minidump_writer::{DumpBuf, MinidumpWriter}; +use crate::sections::MemoryArrayWriter; +use crate::Result; +use std::convert::TryInto; + +/// Write application-provided memory regions. +pub fn write(config: &mut MinidumpWriter, buffer: &mut DumpBuf) -> Result<()> { + for app_memory in &config.app_memory { + let data_copy = LinuxPtraceDumper::copy_from_process( + config.blamed_thread, + app_memory.ptr as *mut libc::c_void, + app_memory.length.try_into()?, + )?; + + let section = MemoryArrayWriter::<u8>::alloc_from_array(buffer, &data_copy)?; + let desc = MDMemoryDescriptor { + start_of_memory_range: app_memory.ptr as u64, + memory: section.location(), + }; + config.memory_blocks.push(desc); + } + Ok(()) +} diff --git a/third_party/rust/minidump_writer_linux/src/sections/exception_stream.rs b/third_party/rust/minidump_writer_linux/src/sections/exception_stream.rs new file mode 100644 index 0000000000..9dd027b5f9 --- /dev/null +++ b/third_party/rust/minidump_writer_linux/src/sections/exception_stream.rs @@ -0,0 +1,95 @@ +use crate::minidump_format::*; +use crate::minidump_writer::{CrashingThreadContext, DumpBuf, MinidumpWriter}; +use crate::sections::MemoryWriter; +use crate::Result; + +#[allow(non_camel_case_types, unused)] +#[repr(u32)] +enum MDExceptionCodeLinux { + MD_EXCEPTION_CODE_LIN_SIGHUP = 1, /* Hangup (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGINT = 2, /* Interrupt (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGQUIT = 3, /* Quit (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGILL = 4, /* Illegal instruction (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGTRAP = 5, /* Trace trap (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGABRT = 6, /* Abort (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGBUS = 7, /* BUS error (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGFPE = 8, /* Floating-point exception (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGKILL = 9, /* Kill, unblockable (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGUSR1 = 10, /* User-defined signal 1 (POSIX). */ + MD_EXCEPTION_CODE_LIN_SIGSEGV = 11, /* Segmentation violation (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGUSR2 = 12, /* User-defined signal 2 (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGPIPE = 13, /* Broken pipe (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGALRM = 14, /* Alarm clock (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTERM = 15, /* Termination (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGSTKFLT = 16, /* Stack faultd */ + MD_EXCEPTION_CODE_LIN_SIGCHLD = 17, /* Child status has changed (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGCONT = 18, /* Continue (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGSTOP = 19, /* Stop, unblockable (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTSTP = 20, /* Keyboard stop (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTTIN = 21, /* Background read from tty (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTTOU = 22, /* Background write to tty (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGURG = 23, + /* Urgent condition on socket (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGXCPU = 24, /* CPU limit exceeded (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGXFSZ = 25, + /* File size limit exceeded (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGVTALRM = 26, /* Virtual alarm clock (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGPROF = 27, /* Profiling alarm clock (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGWINCH = 28, /* Window size change (4.3 BSD, Sun) */ + MD_EXCEPTION_CODE_LIN_SIGIO = 29, /* I/O now possible (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGPWR = 30, /* Power failure restart (System V) */ + MD_EXCEPTION_CODE_LIN_SIGSYS = 31, /* Bad system call */ + MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED = 0xFFFFFFFF, /* No exception, + dump requested. */ +} + +pub fn write(config: &mut MinidumpWriter, buffer: &mut DumpBuf) -> Result<MDRawDirectory> { + let exception = if let Some(context) = &config.crash_context { + MDException { + exception_code: context.siginfo.si_signo as u32, + exception_flags: context.siginfo.si_code as u32, + exception_record: 0, + exception_address: unsafe { context.siginfo.si_addr() } as u64, + number_parameters: 0, + __align: 0, + exception_information: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + } + } else { + let addr = match config.crashing_thread_context { + CrashingThreadContext::CrashContextPlusAddress((_, addr)) => addr, + _ => 0, + }; + MDException { + exception_code: MDExceptionCodeLinux::MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED as u32, + exception_flags: 0, + exception_record: 0, + exception_address: addr as u64, + number_parameters: 0, + __align: 0, + exception_information: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + } + }; + + let thread_context = match config.crashing_thread_context { + CrashingThreadContext::CrashContextPlusAddress((ctx, _)) => ctx, + CrashingThreadContext::CrashContext(ctx) => ctx, + CrashingThreadContext::None => MDLocationDescriptor { + data_size: 0, + rva: 0, + }, + }; + + let stream = MDRawExceptionStream { + thread_id: config.blamed_thread as u32, + exception_record: exception, + __align: 0, + thread_context, + }; + let exc = MemoryWriter::alloc_with_val(buffer, stream)?; + let dirent = MDRawDirectory { + stream_type: MDStreamType::ExceptionStream as u32, + location: exc.location(), + }; + + Ok(dirent) +} diff --git a/third_party/rust/minidump_writer_linux/src/sections/mappings.rs b/third_party/rust/minidump_writer_linux/src/sections/mappings.rs new file mode 100644 index 0000000000..912eb0f3dd --- /dev/null +++ b/third_party/rust/minidump_writer_linux/src/sections/mappings.rs @@ -0,0 +1,102 @@ +use crate::linux_ptrace_dumper::LinuxPtraceDumper; +use crate::maps_reader::MappingInfo; +use crate::minidump_format::*; +use crate::minidump_writer::{DumpBuf, MinidumpWriter}; +use crate::sections::{write_string_to_location, MemoryArrayWriter, MemoryWriter}; +use crate::Result; + +/// Write information about the mappings in effect. Because we are using the +/// minidump format, the information about the mappings is pretty limited. +/// Because of this, we also include the full, unparsed, /proc/$x/maps file in +/// another stream in the file. +pub fn write( + config: &mut MinidumpWriter, + buffer: &mut DumpBuf, + dumper: &mut LinuxPtraceDumper, +) -> Result<MDRawDirectory> { + let mut num_output_mappings = config.user_mapping_list.len(); + + for mapping in &dumper.mappings { + // If the mapping is uninteresting, or if + // there is caller-provided information about this mapping + // in the user_mapping_list list, skip it + if mapping.is_interesting() && !mapping.is_contained_in(&config.user_mapping_list) { + num_output_mappings += 1; + } + } + + let list_header = MemoryWriter::<u32>::alloc_with_val(buffer, num_output_mappings as u32)?; + + let mut dirent = MDRawDirectory { + stream_type: MDStreamType::ModuleListStream as u32, + location: list_header.location(), + }; + + // In case of num_output_mappings == 0, this call doesn't allocate any memory in the buffer + let mut mapping_list = + MemoryArrayWriter::<MDRawModule>::alloc_array(buffer, num_output_mappings)?; + dirent.location.data_size += mapping_list.location().data_size; + + // First write all the mappings from the dumper + let mut idx = 0; + for map_idx in 0..dumper.mappings.len() { + if !dumper.mappings[map_idx].is_interesting() + || dumper.mappings[map_idx].is_contained_in(&config.user_mapping_list) + { + continue; + } + // Note: elf_identifier_for_mapping_index() can manipulate the |mapping.name|. + let identifier = dumper + .elf_identifier_for_mapping_index(map_idx) + .unwrap_or(Default::default()); + let module = fill_raw_module(buffer, &dumper.mappings[map_idx], &identifier)?; + mapping_list.set_value_at(buffer, module, idx)?; + idx += 1; + } + + // Next write all the mappings provided by the caller + for user in &config.user_mapping_list { + // GUID was provided by caller. + let module = fill_raw_module(buffer, &user.mapping, &user.identifier)?; + mapping_list.set_value_at(buffer, module, idx)?; + idx += 1; + } + Ok(dirent) +} + +fn fill_raw_module( + buffer: &mut DumpBuf, + mapping: &MappingInfo, + identifier: &[u8], +) -> Result<MDRawModule> { + let cv_record: MDLocationDescriptor; + if identifier.is_empty() { + // Just zeroes + cv_record = Default::default(); + } else { + let cv_signature = MD_CVINFOELF_SIGNATURE; + let array_size = std::mem::size_of_val(&cv_signature) + identifier.len(); + + let mut sig_section = MemoryArrayWriter::<u8>::alloc_array(buffer, array_size)?; + for (index, val) in cv_signature + .to_ne_bytes() + .iter() + .chain(identifier.iter()) + .enumerate() + { + sig_section.set_value_at(buffer, *val, index)?; + } + cv_record = sig_section.location(); + } + + let (file_path, _) = mapping.get_mapping_effective_name_and_path()?; + let name_header = write_string_to_location(buffer, &file_path)?; + + Ok(MDRawModule { + base_of_image: mapping.start_address as u64, + size_of_image: mapping.size as u32, + cv_record, + module_name_rva: name_header.rva, + ..Default::default() + }) +} diff --git a/third_party/rust/minidump_writer_linux/src/sections/memory_list_stream.rs b/third_party/rust/minidump_writer_linux/src/sections/memory_list_stream.rs new file mode 100644 index 0000000000..a40d2a6b13 --- /dev/null +++ b/third_party/rust/minidump_writer_linux/src/sections/memory_list_stream.rs @@ -0,0 +1,21 @@ +use crate::minidump_format::*; +use crate::minidump_writer::{DumpBuf, MinidumpWriter}; +use crate::sections::{MemoryArrayWriter, MemoryWriter}; +use crate::Result; + +pub fn write(config: &mut MinidumpWriter, buffer: &mut DumpBuf) -> Result<MDRawDirectory> { + let list_header = + MemoryWriter::<u32>::alloc_with_val(buffer, config.memory_blocks.len() as u32)?; + + let mut dirent = MDRawDirectory { + stream_type: MDStreamType::MemoryListStream as u32, + location: list_header.location(), + }; + + let block_list = + MemoryArrayWriter::<MDMemoryDescriptor>::alloc_from_array(buffer, &config.memory_blocks)?; + + dirent.location.data_size += block_list.location().data_size; + + Ok(dirent) +} diff --git a/third_party/rust/minidump_writer_linux/src/sections/mod.rs b/third_party/rust/minidump_writer_linux/src/sections/mod.rs new file mode 100644 index 0000000000..bcddeeef0a --- /dev/null +++ b/third_party/rust/minidump_writer_linux/src/sections/mod.rs @@ -0,0 +1,191 @@ +pub mod app_memory; +pub mod exception_stream; +pub mod mappings; +pub mod memory_list_stream; +pub mod systeminfo_stream; +pub mod thread_list_stream; + +use crate::minidump_format::*; +use crate::Result; +use std::convert::TryInto; +use std::io::{Cursor, Write}; + +#[derive(Debug, PartialEq)] +pub struct MemoryWriter<T: Default + Sized> { + pub position: MDRVA, + pub size: usize, + phantom: std::marker::PhantomData<T>, +} + +impl<T> MemoryWriter<T> +where + T: Default + Sized, +{ + /// Create a slot for a type T in the buffer, we can fill right now with real values. + pub fn alloc_with_val(buffer: &mut Cursor<Vec<u8>>, val: T) -> Result<Self> { + // Get position of this value (e.g. before we add ourselves there) + let position = buffer.position(); + let size = std::mem::size_of::<T>(); + let bytes = unsafe { std::slice::from_raw_parts(&val as *const T as *const u8, size) }; + buffer.write_all(bytes)?; + + Ok(MemoryWriter { + position: position as u32, + size, + phantom: std::marker::PhantomData::<T> {}, + }) + } + + /// Create a slot for a type T in the buffer, we can fill later with real values. + /// This function fills it with `Default::default()`, which is less performant than + /// using uninitialized memory, but safe. + pub fn alloc(buffer: &mut Cursor<Vec<u8>>) -> Result<Self> { + // Filling out the buffer with default-values + let val: T = Default::default(); + Self::alloc_with_val(buffer, val) + } + + /// Write actual values in the buffer-slot we got during `alloc()` + pub fn set_value(&mut self, buffer: &mut Cursor<Vec<u8>>, val: T) -> Result<()> { + // Save whereever the current cursor stands in the buffer + let curr_pos = buffer.position(); + + // Write the actual value we want at our position that + // was determined by `alloc()` into the buffer + buffer.set_position(self.position as u64); + let bytes = unsafe { + std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::<T>()) + }; + let res = buffer.write_all(bytes); + + // Resetting whereever we were before updating this + // regardless of the write-result + buffer.set_position(curr_pos); + + res?; + Ok(()) + } + + pub fn location(&self) -> MDLocationDescriptor { + MDLocationDescriptor { + data_size: std::mem::size_of::<T>() as u32, + rva: self.position, + } + } +} + +#[derive(Debug, PartialEq)] +pub struct MemoryArrayWriter<T: Default + Sized> { + pub position: MDRVA, + array_size: usize, + phantom: std::marker::PhantomData<T>, +} + +impl<T> MemoryArrayWriter<T> +where + T: Default + Sized, +{ + /// Create a slot for a type T in the buffer, we can fill in the values in one go. + pub fn alloc_from_array(buffer: &mut Cursor<Vec<u8>>, array: &[T]) -> Result<Self> { + // Get position of this value (e.g. before we add ourselves there) + let position = buffer.position(); + for val in array { + let bytes = unsafe { + std::slice::from_raw_parts(val as *const T as *const u8, std::mem::size_of::<T>()) + }; + buffer.write_all(bytes)?; + } + + Ok(MemoryArrayWriter { + position: position as u32, + array_size: array.len(), + phantom: std::marker::PhantomData::<T> {}, + }) + } + + /// Create a slot for a type T in the buffer, we can fill later with real values. + /// This function fills it with `Default::default()`, which is less performant than + /// using uninitialized memory, but safe. + pub fn alloc_array(buffer: &mut Cursor<Vec<u8>>, array_size: usize) -> Result<Self> { + // Get position of this value (e.g. before we add ourselves there) + let position = buffer.position(); + for _ in 0..array_size { + // Filling out the buffer with default-values + let val: T = Default::default(); + let bytes = unsafe { + std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::<T>()) + }; + buffer.write_all(bytes)?; + } + + Ok(MemoryArrayWriter { + position: position as u32, + array_size, + phantom: std::marker::PhantomData::<T> {}, + }) + } + + /// Write actual values in the buffer-slot we got during `alloc()` + pub fn set_value_at( + &mut self, + buffer: &mut Cursor<Vec<u8>>, + val: T, + index: usize, + ) -> Result<()> { + // Save whereever the current cursor stands in the buffer + let curr_pos = buffer.position(); + + // Write the actual value we want at our position that + // was determined by `alloc()` into the buffer + buffer.set_position(self.position as u64 + (std::mem::size_of::<T>() * index) as u64); + let bytes = unsafe { + std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::<T>()) + }; + let res = buffer.write_all(bytes); + + // Resetting whereever we were before updating this + // regardless of the write-result + buffer.set_position(curr_pos); + + res?; + Ok(()) + } + + pub fn location(&self) -> MDLocationDescriptor { + MDLocationDescriptor { + data_size: (self.array_size * std::mem::size_of::<T>()) as u32, + rva: self.position, + } + } + + pub fn location_of_index(&self, idx: usize) -> MDLocationDescriptor { + MDLocationDescriptor { + data_size: std::mem::size_of::<T>() as u32, + rva: self.position + (std::mem::size_of::<T>() * idx) as u32, + } + } +} + +pub fn write_string_to_location( + buffer: &mut Cursor<Vec<u8>>, + text: &str, +) -> Result<MDLocationDescriptor> { + let letters: Vec<u16> = text.encode_utf16().collect(); + + // First write size of the string (x letters in u16, times the size of u16) + let text_header = MemoryWriter::<u32>::alloc_with_val( + buffer, + (letters.len() * std::mem::size_of::<u16>()).try_into()?, + )?; + + // Then write utf-16 letters after that + let mut text_section = MemoryArrayWriter::<u16>::alloc_array(buffer, letters.len())?; + for (index, letter) in letters.iter().enumerate() { + text_section.set_value_at(buffer, *letter, index)?; + } + + let mut location = text_header.location(); + location.data_size += text_section.location().data_size; + + Ok(location) +} diff --git a/third_party/rust/minidump_writer_linux/src/sections/systeminfo_stream.rs b/third_party/rust/minidump_writer_linux/src/sections/systeminfo_stream.rs new file mode 100644 index 0000000000..2d17edbcd7 --- /dev/null +++ b/third_party/rust/minidump_writer_linux/src/sections/systeminfo_stream.rs @@ -0,0 +1,19 @@ +use crate::dumper_cpu_info::{write_cpu_information, write_os_information}; +use crate::minidump_format::*; +use crate::minidump_writer::DumpBuf; +use crate::sections::MemoryWriter; +use crate::Result; + +pub fn write(buffer: &mut DumpBuf) -> Result<MDRawDirectory> { + let mut info_section = MemoryWriter::<MDRawSystemInfo>::alloc(buffer)?; + let dirent = MDRawDirectory { + stream_type: MDStreamType::SystemInfoStream as u32, + location: info_section.location(), + }; + let mut info: MDRawSystemInfo = Default::default(); + write_cpu_information(&mut info)?; + write_os_information(buffer, &mut info)?; + + info_section.set_value(buffer, info)?; + Ok(dirent) +} diff --git a/third_party/rust/minidump_writer_linux/src/sections/thread_list_stream.rs b/third_party/rust/minidump_writer_linux/src/sections/thread_list_stream.rs new file mode 100644 index 0000000000..0abd436c2c --- /dev/null +++ b/third_party/rust/minidump_writer_linux/src/sections/thread_list_stream.rs @@ -0,0 +1,235 @@ +use crate::linux_ptrace_dumper::LinuxPtraceDumper; +use crate::minidump_cpu::RawContextCPU; +use crate::minidump_format::*; +use crate::minidump_writer::{CrashingThreadContext, DumpBuf, MinidumpWriter}; +use crate::sections::{MemoryArrayWriter, MemoryWriter}; +use crate::Result; +use std::convert::TryInto; +use std::io::Write; + +// The following kLimit* constants are for when minidump_size_limit_ is set +// and the minidump size might exceed it. +// +// Estimate for how big each thread's stack will be (in bytes). +const LIMIT_AVERAGE_THREAD_STACK_LENGTH: usize = 8 * 1024; +// Number of threads whose stack size we don't want to limit. These base +// threads will simply be the first N threads returned by the dumper (although +// the crashing thread will never be limited). Threads beyond this count are +// the extra threads. +const LIMIT_BASE_THREAD_COUNT: usize = 20; +// Maximum stack size to dump for any extra thread (in bytes). +const LIMIT_MAX_EXTRA_THREAD_STACK_LEN: usize = 2 * 1024; +// Make sure this number of additional bytes can fit in the minidump +// (exclude the stack data). +const LIMIT_MINIDUMP_FUDGE_FACTOR: u64 = 64 * 1024; + +#[derive(Debug, Clone, Copy)] +enum MaxStackLen { + None, + Len(usize), +} + +pub fn write( + config: &mut MinidumpWriter, + buffer: &mut DumpBuf, + dumper: &LinuxPtraceDumper, +) -> Result<MDRawDirectory> { + let num_threads = dumper.threads.len(); + // Memory looks like this: + // <num_threads><thread_1><thread_2>... + + let list_header = MemoryWriter::<u32>::alloc_with_val(buffer, num_threads as u32)?; + + let mut dirent = MDRawDirectory { + stream_type: MDStreamType::ThreadListStream as u32, + location: list_header.location(), + }; + + let mut thread_list = MemoryArrayWriter::<MDRawThread>::alloc_array(buffer, num_threads)?; + dirent.location.data_size += thread_list.location().data_size; + // If there's a minidump size limit, check if it might be exceeded. Since + // most of the space is filled with stack data, just check against that. + // If this expects to exceed the limit, set extra_thread_stack_len such + // that any thread beyond the first kLimitBaseThreadCount threads will + // have only kLimitMaxExtraThreadStackLen bytes dumped. + let mut extra_thread_stack_len = MaxStackLen::None; // default to no maximum + if let Some(minidump_size_limit) = config.minidump_size_limit { + let estimated_total_stack_size = (num_threads * LIMIT_AVERAGE_THREAD_STACK_LENGTH) as u64; + let curr_pos = buffer.position(); + let estimated_minidump_size = + curr_pos + estimated_total_stack_size + LIMIT_MINIDUMP_FUDGE_FACTOR; + if estimated_minidump_size > minidump_size_limit { + extra_thread_stack_len = MaxStackLen::Len(LIMIT_MAX_EXTRA_THREAD_STACK_LEN); + } + } + + for (idx, item) in dumper.threads.clone().iter().enumerate() { + let mut thread = MDRawThread::default(); + thread.thread_id = (*item).try_into()?; + + // We have a different source of information for the crashing thread. If + // we used the actual state of the thread we would find it running in the + // signal handler with the alternative stack, which would be deeply + // unhelpful. + if config.crash_context.is_some() && thread.thread_id == config.blamed_thread as u32 { + let crash_context = config.crash_context.as_ref().unwrap(); + let instruction_ptr = crash_context.get_instruction_pointer() as usize; + let stack_pointer = crash_context.get_stack_pointer() as usize; + fill_thread_stack( + config, + buffer, + dumper, + &mut thread, + instruction_ptr, + stack_pointer, + MaxStackLen::None, + )?; + // Copy 256 bytes around crashing instruction pointer to minidump. + let ip_memory_size: usize = 256; + // Bound it to the upper and lower bounds of the memory map + // it's contained within. If it's not in mapped memory, + // don't bother trying to write it. + for mapping in &dumper.mappings { + if instruction_ptr < mapping.start_address + || instruction_ptr >= mapping.start_address + mapping.size + { + continue; + } + + let mut ip_memory_d: MDMemoryDescriptor = Default::default(); + + // Try to get 128 bytes before and after the IP, but + // settle for whatever's available. + ip_memory_d.start_of_memory_range = + std::cmp::max(mapping.start_address, instruction_ptr - ip_memory_size / 2) + as u64; + let end_of_range = std::cmp::min( + mapping.start_address + mapping.size, + instruction_ptr + ip_memory_size / 2, + ) as u64; + ip_memory_d.memory.data_size = + (end_of_range - ip_memory_d.start_of_memory_range) as u32; + + let memory_copy = LinuxPtraceDumper::copy_from_process( + thread.thread_id as i32, + ip_memory_d.start_of_memory_range as *mut libc::c_void, + ip_memory_d.memory.data_size as isize, + )?; + + let mem_section = MemoryArrayWriter::alloc_from_array(buffer, &memory_copy)?; + ip_memory_d.memory = mem_section.location(); + config.memory_blocks.push(ip_memory_d); + + break; + } + // let cpu = MemoryWriter::alloc(buffer, &memory_copy)?; + let mut cpu: RawContextCPU = Default::default(); + let crash_context = config.crash_context.as_ref().unwrap(); + crash_context.fill_cpu_context(&mut cpu); + let cpu_section = MemoryWriter::alloc_with_val(buffer, cpu)?; + thread.thread_context = cpu_section.location(); + + config.crashing_thread_context = + CrashingThreadContext::CrashContext(cpu_section.location()); + } else { + let info = dumper.get_thread_info_by_index(idx)?; + let max_stack_len = + if config.minidump_size_limit.is_some() && idx >= LIMIT_BASE_THREAD_COUNT { + extra_thread_stack_len + } else { + MaxStackLen::None // default to no maximum for this thread + }; + let instruction_ptr = info.get_instruction_pointer() as usize; + fill_thread_stack( + config, + buffer, + dumper, + &mut thread, + instruction_ptr, + info.stack_pointer, + max_stack_len, + )?; + + let mut cpu = RawContextCPU::default(); + info.fill_cpu_context(&mut cpu); + let cpu_section = MemoryWriter::<RawContextCPU>::alloc_with_val(buffer, cpu)?; + thread.thread_context = cpu_section.location(); + if item == &config.blamed_thread { + // This is the crashing thread of a live process, but + // no context was provided, so set the crash address + // while the instruction pointer is already here. + config.crashing_thread_context = CrashingThreadContext::CrashContextPlusAddress(( + cpu_section.location(), + info.get_instruction_pointer(), + )); + } + } + thread_list.set_value_at(buffer, thread, idx)?; + } + Ok(dirent) +} + +fn fill_thread_stack( + config: &mut MinidumpWriter, + buffer: &mut DumpBuf, + dumper: &LinuxPtraceDumper, + thread: &mut MDRawThread, + instruction_ptr: usize, + stack_ptr: usize, + max_stack_len: MaxStackLen, +) -> Result<()> { + thread.stack.start_of_memory_range = stack_ptr.try_into()?; + thread.stack.memory.data_size = 0; + thread.stack.memory.rva = buffer.position() as u32; + + if let Ok((mut stack, mut stack_len)) = dumper.get_stack_info(stack_ptr) { + if let MaxStackLen::Len(max_stack_len) = max_stack_len { + if stack_len > max_stack_len { + stack_len = max_stack_len; + + // Skip empty chunks of length max_stack_len. + // Meaning != 0 + if stack_len > 0 { + while stack + stack_len < stack_ptr { + stack += stack_len; + } + } + } + } + + let mut stack_bytes = LinuxPtraceDumper::copy_from_process( + thread.thread_id.try_into()?, + stack as *mut libc::c_void, + stack_len.try_into()?, + )?; + let stack_pointer_offset = stack_ptr - stack; + if config.skip_stacks_if_mapping_unreferenced { + if let Some(principal_mapping) = &config.principal_mapping { + let low_addr = principal_mapping.system_mapping_info.start_address; + let high_addr = principal_mapping.system_mapping_info.end_address; + if (instruction_ptr < low_addr || instruction_ptr > high_addr) + && !principal_mapping + .stack_has_pointer_to_mapping(&stack_bytes, stack_pointer_offset) + { + return Ok(()); + } + } else { + return Ok(()); + } + } + + if config.sanitize_stack { + dumper.sanitize_stack_copy(&mut stack_bytes, stack_ptr, stack_pointer_offset)?; + } + + let stack_location = MDLocationDescriptor { + data_size: stack_bytes.len() as u32, + rva: buffer.position() as u32, + }; + buffer.write_all(&stack_bytes)?; + thread.stack.start_of_memory_range = stack as u64; + thread.stack.memory = stack_location; + config.memory_blocks.push(thread.stack.clone()); + } + Ok(()) +} |