commit 4d46c93414e59d8dc40bf62d76138cb3a11714c1
parent ab3a40824c9de18e1c97bad0b541bd6f024d624c
Author: Gabriele Svelto <gsvelto@mozilla.com>
Date: Wed, 17 Dec 2025 10:26:00 +0000
Bug 2002785 - Split platform-specific parts of the crash helper r=afranchuk
This moves a bunch of platform-specific code into separate files to
reduce the amount of platform-specific bits in the shared logic. The
only exception being the IPC server. I'm moving the message handling out
of the crash generation logic and into the server as subsequent changes
for mach IPC require more complex interaction when dealing with certain
types of messages. My hope of cleanly splitting IPC message handling
from crash logic turned out to be too optimistic.
Differential Revision: https://phabricator.services.mozilla.com/D275945
Diffstat:
4 files changed, 150 insertions(+), 175 deletions(-)
diff --git a/toolkit/crashreporter/crash_helper_server/src/breakpad_crash_generator.rs b/toolkit/crashreporter/crash_helper_server/src/breakpad_crash_generator.rs
@@ -6,27 +6,58 @@
* Wrappers used to call into Breakpad code *
******************************************************************************/
-use std::{
- ffi::{c_char, c_void, OsString},
- ptr::NonNull,
-};
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use super::crash_generation::get_auxv_info;
use anyhow::{bail, Result};
use cfg_if::cfg_if;
-use crash_helper_common::{BreakpadChar, BreakpadData, BreakpadString};
+use crash_helper_common::{BreakpadChar, BreakpadData, BreakpadString, Pid};
#[cfg(any(target_os = "android", target_os = "linux"))]
use minidump_writer::minidump_writer::DirectAuxvDumpInfo;
-
-use crate::crash_generation::BreakpadProcessId;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use std::os::fd::{FromRawFd, OwnedFd};
+use std::{
+ ffi::{c_char, c_void, OsString},
+ ptr::NonNull,
+};
#[cfg(target_os = "windows")]
type BreakpadInitType = *const u16;
+#[cfg(target_os = "windows")]
+type NativeProcessId = windows_sys::Win32::Foundation::HANDLE;
+
#[cfg(target_os = "macos")]
type BreakpadInitType = *const c_char;
+#[cfg(target_os = "macos")]
+type NativeProcessId = u32;
+
#[cfg(any(target_os = "linux", target_os = "android"))]
type BreakpadInitType = std::os::fd::RawFd;
#[cfg(any(target_os = "linux", target_os = "android"))]
-use std::os::fd::{FromRawFd, OwnedFd};
+type NativeProcessId = Pid;
+
+#[repr(C)]
+pub struct BreakpadProcessId {
+ pub pid: Pid,
+ #[cfg(target_os = "macos")]
+ pub task: u32,
+ #[cfg(target_os = "windows")]
+ pub handle: windows_sys::Win32::Foundation::HANDLE,
+}
+
+impl BreakpadProcessId {
+ pub fn get_native(&self) -> NativeProcessId {
+ cfg_if! {
+ if #[cfg(any(target_os = "linux", target_os = "android"))] {
+ self.pid
+ } else if #[cfg(target_os = "windows")] {
+ self.handle
+ } else if #[cfg(target_os = "macos")] {
+ self.task
+ }
+ }
+ }
+}
extern "C" {
fn CrashGenerationServer_init(
@@ -68,11 +99,6 @@ impl BreakpadCrashGenerator {
breakpad_data: BreakpadData,
path: OsString,
finalize_callback: extern "C" fn(BreakpadProcessId, *const c_char, *const BreakpadChar),
- #[cfg(any(target_os = "android", target_os = "linux"))]
- auxv_callback: extern "C" fn(
- crash_helper_common::Pid,
- *mut DirectAuxvDumpInfo,
- ) -> bool,
) -> Result<BreakpadCrashGenerator> {
let breakpad_raw_data = breakpad_data.into_raw();
let path_ptr = path.into_raw();
@@ -84,7 +110,7 @@ impl BreakpadCrashGenerator {
path_ptr,
finalize_callback,
#[cfg(any(target_os = "android", target_os = "linux"))]
- auxv_callback,
+ get_auxv_info,
)
};
diff --git a/toolkit/crashreporter/crash_helper_server/src/crash_generation.rs b/toolkit/crashreporter/crash_helper_server/src/crash_generation.rs
@@ -6,19 +6,24 @@ pub mod crash_annotations {
include!(concat!(env!("OUT_DIR"), "/crash_annotations.rs"));
}
+use super::{
+ breakpad_crash_generator::{BreakpadCrashGenerator, BreakpadProcessId},
+ phc::{self, StackTrace},
+};
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod linux;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub(crate) use linux::get_auxv_info;
+
#[cfg(target_os = "windows")]
mod windows;
-use anyhow::{bail, Result};
+use anyhow::Result;
use crash_annotations::{
should_include_annotation, type_of_annotation, CrashAnnotation, CrashAnnotationType,
};
-use crash_helper_common::{
- messages::{self, Message},
- AncillaryData, BreakpadChar, BreakpadData, BreakpadString, IPCConnector, Pid,
-};
-#[cfg(any(target_os = "android", target_os = "linux"))]
-use minidump_writer::minidump_writer::DirectAuxvDumpInfo;
+use crash_helper_common::{messages, BreakpadChar, BreakpadData, BreakpadString, Pid};
use mozannotation_server::{AnnotationData, CAnnotation};
use num_traits::FromPrimitive;
use once_cell::sync::Lazy;
@@ -30,16 +35,8 @@ use std::{
io::{Seek, SeekFrom, Write},
mem::size_of,
path::{Path, PathBuf},
- process,
sync::Mutex,
};
-#[cfg(target_os = "windows")]
-use windows_sys::Win32::Foundation::HANDLE;
-
-use crate::{
- breakpad_crash_generator::BreakpadCrashGenerator,
- phc::{self, StackTrace},
-};
struct CrashReport {
path: OsString,
@@ -61,11 +58,6 @@ impl CrashReport {
// order they've arrived.
static CRASH_REPORTS: Lazy<Mutex<HashMap<Pid, Vec<CrashReport>>>> = Lazy::new(Default::default);
-// Table holding the information about the auxiliary vector of potentially
-// every process registered with the crash helper.
-#[cfg(any(target_os = "android", target_os = "linux"))]
-static AUXV_INFO_MAP: Lazy<Mutex<HashMap<Pid, DirectAuxvDumpInfo>>> = Lazy::new(Default::default);
-
/******************************************************************************
* Crash generator *
******************************************************************************/
@@ -76,11 +68,6 @@ enum MinidumpOrigin {
WindowsErrorReporting,
}
-pub(crate) enum MessageResult {
- None,
- Connection(IPCConnector),
-}
-
pub(crate) struct CrashGenerator {
// This will be used for generating hangs
_minidump_path: OsString,
@@ -96,8 +83,6 @@ impl CrashGenerator {
breakpad_data,
minidump_path.clone(),
finalize_breakpad_minidump,
- #[cfg(any(target_os = "android", target_os = "linux"))]
- get_auxv_info,
)?;
Ok(CrashGenerator {
@@ -106,98 +91,11 @@ impl CrashGenerator {
})
}
- // Process a message received from the parent process. Return an optional
- // reply that will be sent back to the parent.
- pub(crate) fn parent_message(
- &mut self,
- client: &IPCConnector,
- kind: messages::Kind,
- data: &[u8],
- ancillary_data: Option<AncillaryData>,
- ) -> Result<MessageResult> {
- match kind {
- messages::Kind::SetCrashReportPath => {
- let message = messages::SetCrashReportPath::decode(data, ancillary_data)?;
- self.set_path(message.path);
- Ok(MessageResult::None)
- }
- messages::Kind::TransferMinidump => {
- let message = messages::TransferMinidump::decode(data, ancillary_data)?;
- client.send_message(self.transfer_minidump(message.pid))?;
- Ok(MessageResult::None)
- }
- messages::Kind::GenerateMinidump => {
- todo!("Implement all messages");
- }
- #[cfg(any(target_os = "android", target_os = "linux"))]
- messages::Kind::RegisterAuxvInfo => {
- let message = messages::RegisterAuxvInfo::decode(data, ancillary_data)?;
- let map = &mut AUXV_INFO_MAP.lock().unwrap();
- map.insert(message.pid, message.auxv_info);
-
- Ok(MessageResult::None)
- }
- #[cfg(any(target_os = "android", target_os = "linux"))]
- messages::Kind::UnregisterAuxvInfo => {
- let message = messages::UnregisterAuxvInfo::decode(data, ancillary_data)?;
- let map = &mut AUXV_INFO_MAP.lock().unwrap();
- map.remove(&message.pid);
-
- Ok(MessageResult::None)
- }
- messages::Kind::RegisterChildProcess => {
- let message = messages::RegisterChildProcess::decode(data, ancillary_data)?;
- let connector = IPCConnector::from_ancillary(message.ipc_endpoint)?;
- connector
- .send_message(messages::ChildProcessRegistered::new(process::id() as Pid))?;
- Ok(MessageResult::Connection(connector))
- }
- kind => {
- bail!("Unexpected message {kind:?} from parent process");
- }
- }
- }
-
- // Process a message received from a child process. Return an optional
- // reply that will be sent back to the child.
- pub(crate) fn child_message(
- &mut self,
- kind: messages::Kind,
- _data: &[u8],
- _ancillary_data: Option<AncillaryData>,
- ) -> Result<MessageResult> {
- bail!("Unexpected message {kind:?} from child process");
- }
-
- // Process a message received from an external process. Return an optional
- // reply that will be sent back.
- pub(crate) fn external_message(
- &mut self,
- #[allow(unused_variables)] runtime: &IPCConnector,
- kind: messages::Kind,
- #[allow(unused_variables)] data: &[u8],
- #[allow(unused_variables)] ancillary_data: Option<AncillaryData>,
- ) -> Result<MessageResult> {
- match kind {
- #[cfg(target_os = "windows")]
- messages::Kind::WindowsErrorReporting => {
- let message =
- messages::WindowsErrorReportingMinidump::decode(data, ancillary_data)?;
- let _ = self.generate_wer_minidump(message);
- runtime.send_message(messages::WindowsErrorReportingMinidumpReply::new())?;
- Ok(MessageResult::None)
- }
- kind => {
- bail!("Unexpected message {kind:?} from external process");
- }
- }
- }
-
- fn set_path(&mut self, path: OsString) {
+ pub(crate) fn set_path(&mut self, path: OsString) {
self.breakpad_server.set_path(path);
}
- fn transfer_minidump(&self, pid: Pid) -> messages::TransferMinidumpReply {
+ pub(crate) fn retrieve_minidump(&self, pid: Pid) -> messages::TransferMinidumpReply {
let mut map = CRASH_REPORTS.lock().unwrap();
if let Some(mut entry) = map.remove(&pid) {
let crash_report = entry.remove(0);
@@ -282,15 +180,6 @@ fn serialize_phc_stack(stack_trace: &StackTrace) -> String {
string
}
-#[repr(C)]
-pub struct BreakpadProcessId {
- pub pid: Pid,
- #[cfg(target_os = "macos")]
- pub task: u32,
- #[cfg(target_os = "windows")]
- pub handle: HANDLE,
-}
-
/// This reads the crash annotations, writes them to the .extra file and
/// finally stores the resulting minidump in the global hash table.
extern "C" fn finalize_breakpad_minidump(
@@ -338,34 +227,12 @@ fn finalize_crash_report(
.or_insert_with(|| vec![CrashReport::new(path, &error)]);
}
-#[cfg(any(target_os = "android", target_os = "linux"))]
-extern "C" fn get_auxv_info(pid: Pid, auxv_info_ptr: *mut DirectAuxvDumpInfo) -> bool {
- let map = &mut AUXV_INFO_MAP.lock().unwrap();
-
- if let Some(auxv_info) = map.get(&pid) {
- // SAFETY: The auxv_info_ptr is guaranteed to be valid by the caller.
- unsafe { auxv_info_ptr.write(auxv_info.to_owned()) };
- true
- } else {
- false
- }
-}
-
fn retrieve_annotations(
process_id: &BreakpadProcessId,
origin: MinidumpOrigin,
) -> Result<Vec<CAnnotation>> {
- #[cfg(target_os = "windows")]
- let res = mozannotation_server::retrieve_annotations(
- process_id.handle,
- CrashAnnotation::Count as usize,
- );
- #[cfg(any(target_os = "linux", target_os = "android"))]
- let res =
- mozannotation_server::retrieve_annotations(process_id.pid, CrashAnnotation::Count as usize);
- #[cfg(target_os = "macos")]
let res = mozannotation_server::retrieve_annotations(
- process_id.task,
+ process_id.get_native(),
CrashAnnotation::Count as usize,
);
diff --git a/toolkit/crashreporter/crash_helper_server/src/crash_generation/linux.rs b/toolkit/crashreporter/crash_helper_server/src/crash_generation/linux.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CrashGenerator;
+
+use anyhow::Result;
+use crash_helper_common::{
+ messages::{self},
+ Pid,
+};
+use minidump_writer::minidump_writer::DirectAuxvDumpInfo;
+use once_cell::sync::Lazy;
+use std::{collections::HashMap, sync::Mutex};
+
+// Table holding the information about the auxiliary vector of potentially
+// every process registered with the crash helper.
+static AUXV_INFO_MAP: Lazy<Mutex<HashMap<Pid, DirectAuxvDumpInfo>>> = Lazy::new(Default::default);
+
+impl CrashGenerator {
+ pub(crate) fn register_auxv_info(&self, message: messages::RegisterAuxvInfo) -> Result<()> {
+ let map = &mut AUXV_INFO_MAP.lock().unwrap();
+ map.insert(message.pid, message.auxv_info);
+ Ok(())
+ }
+
+ pub(crate) fn unregister_auxv_info(&self, message: messages::UnregisterAuxvInfo) -> Result<()> {
+ let map = &mut AUXV_INFO_MAP.lock().unwrap();
+ map.remove(&message.pid);
+ Ok(())
+ }
+}
+
+pub(crate) extern "C" fn get_auxv_info(pid: Pid, auxv_info_ptr: *mut DirectAuxvDumpInfo) -> bool {
+ let map = &mut AUXV_INFO_MAP.lock().unwrap();
+
+ if let Some(auxv_info) = map.get(&pid) {
+ // SAFETY: The auxv_info_ptr is guaranteed to be valid by the caller.
+ unsafe { auxv_info_ptr.write(auxv_info.to_owned()) };
+ true
+ } else {
+ false
+ }
+}
diff --git a/toolkit/crashreporter/crash_helper_server/src/ipc_server.rs b/toolkit/crashreporter/crash_helper_server/src/ipc_server.rs
@@ -2,13 +2,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-use anyhow::Result;
+use anyhow::{bail, Result};
use crash_helper_common::{
- messages::Header, AncillaryData, IPCConnector, IPCConnectorKey, IPCEvent, IPCListener, IPCQueue,
+ messages::{self, Header, Message},
+ AncillaryData, IPCConnector, IPCConnectorKey, IPCEvent, IPCListener, IPCQueue, Pid,
};
-use std::{collections::HashMap, rc::Rc};
+use std::{collections::HashMap, process, rc::Rc};
-use crate::crash_generation::{CrashGenerator, MessageResult};
+use crate::crash_generation::CrashGenerator;
#[derive(PartialEq)]
pub enum IPCServerState {
@@ -112,10 +113,25 @@ impl IPCServer {
let connector = &connection.connector;
match connection.endpoint {
- IPCEndpoint::Parent => {
- let res =
- generator.parent_message(connector, header.kind, &data, ancillary_data)?;
- if let MessageResult::Connection(connector) = res {
+ IPCEndpoint::Parent => match header.kind {
+ messages::Kind::SetCrashReportPath => {
+ let message = messages::SetCrashReportPath::decode(&data, ancillary_data)?;
+ generator.set_path(message.path);
+ }
+ messages::Kind::TransferMinidump => {
+ let message = messages::TransferMinidump::decode(&data, ancillary_data)?;
+ connector.send_message(generator.retrieve_minidump(message.pid))?;
+ }
+ messages::Kind::GenerateMinidump => {
+ todo!("Implement all messages");
+ }
+ messages::Kind::RegisterChildProcess => {
+ let message = messages::RegisterChildProcess::decode(&data, ancillary_data)?;
+ let connector = IPCConnector::from_ancillary(message.ipc_endpoint)?;
+ connector.send_message(messages::ChildProcessRegistered::new(
+ process::id() as Pid
+ ))?;
+
let connector = Rc::new(connector);
self.queue.add_connector(&connector)?;
self.connections.insert(
@@ -126,13 +142,35 @@ impl IPCServer {
},
);
}
- }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ messages::Kind::RegisterAuxvInfo => {
+ let message = messages::RegisterAuxvInfo::decode(&data, ancillary_data)?;
+ generator.register_auxv_info(message)?;
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ messages::Kind::UnregisterAuxvInfo => {
+ let message = messages::UnregisterAuxvInfo::decode(&data, ancillary_data)?;
+ generator.unregister_auxv_info(message)?;
+ }
+ kind => {
+ bail!("Unexpected message {kind:?} from parent process");
+ }
+ },
IPCEndpoint::Child => {
- generator.child_message(header.kind, &data, ancillary_data)?;
- }
- IPCEndpoint::External => {
- generator.external_message(connector, header.kind, &data, ancillary_data)?;
+ bail!("Unexpected message {:?} from child process", header.kind);
}
+ IPCEndpoint::External => match header.kind {
+ #[cfg(target_os = "windows")]
+ messages::Kind::WindowsErrorReporting => {
+ let message =
+ messages::WindowsErrorReportingMinidump::decode(data, ancillary_data)?;
+ generator.generate_wer_minidump(message);
+ connector.send_message(messages::WindowsErrorReportingMinidumpReply::new())?;
+ }
+ kind => {
+ bail!("Unexpected message {kind:?} from external process");
+ }
+ },
};
Ok(())