summaryrefslogtreecommitdiffstats
path: root/third_party/rust/minidump_writer_linux/src/sections
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/minidump_writer_linux/src/sections')
-rw-r--r--third_party/rust/minidump_writer_linux/src/sections/app_memory.rs25
-rw-r--r--third_party/rust/minidump_writer_linux/src/sections/exception_stream.rs95
-rw-r--r--third_party/rust/minidump_writer_linux/src/sections/mappings.rs102
-rw-r--r--third_party/rust/minidump_writer_linux/src/sections/memory_list_stream.rs21
-rw-r--r--third_party/rust/minidump_writer_linux/src/sections/mod.rs191
-rw-r--r--third_party/rust/minidump_writer_linux/src/sections/systeminfo_stream.rs19
-rw-r--r--third_party/rust/minidump_writer_linux/src/sections/thread_list_stream.rs235
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(())
+}