diff options
Diffstat (limited to 'toolkit/crashreporter')
26 files changed, 1095 insertions, 435 deletions
diff --git a/toolkit/crashreporter/CrashAnnotations.h.in b/toolkit/crashreporter/CrashAnnotations.h.in index 7ea8c98bc1..2288498f36 100644 --- a/toolkit/crashreporter/CrashAnnotations.h.in +++ b/toolkit/crashreporter/CrashAnnotations.h.in @@ -50,6 +50,16 @@ ${skiplist} }; /** + * Return the type of a crash annotation. + * + * @param aAnnotation a crash annotation + * @returns The type of this annotation + */ +static inline AnnotationType TypeOfAnnotation(Annotation aAnnotation) { + return kAnnotationTypes[static_cast<uint32_t>(aAnnotation)]; +} + +/** * Return the string representation of a crash annotation. * * @param aAnnotation a crash annotation @@ -97,9 +107,17 @@ class AnnotationWriter { public: virtual void Write(Annotation aAnnotation, const char* aValue, size_t aLen = 0) = 0; + virtual void Write(Annotation aAnnotation, bool aValue) = 0; virtual void Write(Annotation aAnnotation, uint64_t aValue) = 0; }; +#ifdef XP_WIN + +extern void RecordDllAnnotations(bool* aBlocklistInitFailed, + bool* aUser32BeforeBlocklist); + +#endif // XP_WIN + } // namespace CrashReporter #endif // CrashAnnotations_h diff --git a/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.cc b/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.cc index 8f25b7be02..2c2e24e071 100644 --- a/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.cc +++ b/toolkit/crashreporter/breakpad-client/linux/microdump_writer/microdump_writer.cc @@ -466,8 +466,8 @@ class MicrodumpWriter { char file_name[NAME_MAX]; char file_path[NAME_MAX]; - dumper_->GetMappingEffectiveNameAndPath( - mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); + dumper_->GetMappingEffectiveNamePathAndVersion( + mapping, file_path, sizeof(file_path), file_name, sizeof(file_name), nullptr); LogAppend("M "); LogAppend(static_cast<uintptr_t>(mapping.start_addr)); diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.cc index 3b400ce8ca..ef05583397 100644 --- a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.cc +++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.cc @@ -468,11 +468,12 @@ bool ElfFileSoName(const LinuxDumper& dumper, } // namespace -void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, +void LinuxDumper::GetMappingEffectiveNamePathAndVersion(const MappingInfo& mapping, char* file_path, size_t file_path_size, char* file_name, - size_t file_name_size) { + size_t file_name_size, + uint32_t* version) { my_strlcpy(file_path, mapping.name, file_path_size); // Tools such as minidump_stackwalk use the name of the module to look up @@ -487,6 +488,9 @@ void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, const char* basename = my_strrchr(file_path, '/'); basename = basename == NULL ? file_path : (basename + 1); my_strlcpy(file_name, basename, file_name_size); + if (version) { + ElfFileSoVersion(mapping.name, version); + } return; } @@ -512,6 +516,10 @@ void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, my_strlcpy(file_path, file_name, file_path_size); } } + + if (version) { + ElfFileSoVersion(mapping.name, version); + } } bool LinuxDumper::ReadAuxv() { diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.h b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.h index 7155524ffc..7fd0693968 100644 --- a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.h +++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_dumper.h @@ -56,6 +56,10 @@ #include "common/memory_allocator.h" #include "google_breakpad/common/minidump_format.h" +#if defined(XP_LINUX) +# include "linux_utils.h" +#endif // defined(XP_LINUX) + namespace google_breakpad { // Typedef for our parsing of the auxv variables in /proc/pid/auxv. @@ -213,11 +217,12 @@ class LinuxDumper { // other cases, however, a library can be mapped from an archive (e.g., when // loading .so libs from an apk on Android) and this method is able to // reconstruct the original file name. - void GetMappingEffectiveNameAndPath(const MappingInfo& mapping, + void GetMappingEffectiveNamePathAndVersion(const MappingInfo& mapping, char* file_path, size_t file_path_size, char* file_name, - size_t file_name_size); + size_t file_name_size, + uint32_t* version); protected: bool ReadAuxv(); diff --git a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc index 03066e9110..60d1195949 100644 --- a/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc +++ b/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc @@ -717,8 +717,16 @@ class MinidumpWriter { char file_name[NAME_MAX]; char file_path[NAME_MAX]; - dumper_->GetMappingEffectiveNameAndPath( - mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); + uint32_t version[4] = { 0, 0, 0, 0 }; + dumper_->GetMappingEffectiveNamePathAndVersion( + mapping, file_path, sizeof(file_path), file_name, sizeof(file_name), version); + + mod->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE; + mod->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION; + mod->version_info.file_version_hi = version[0]; + mod->version_info.file_version_lo = version[1]; + mod->version_info.product_version_hi = version[2]; + mod->version_info.product_version_lo = version[3]; MDLocationDescriptor ld; if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld)) diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc index e13e4509b0..64c01bdce4 100644 --- a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc +++ b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc @@ -83,7 +83,7 @@ MinidumpGenerator::MinidumpGenerator() cpu_type_(DynamicImages::GetNativeCPUType()), dyldImageLoadAddress_(NULL), dyldSlide_(0), - dyldPath_(), + dyldPath_(nullptr), task_context_(NULL), dynamic_images_(NULL), memory_blocks_(&allocator_) { @@ -105,7 +105,7 @@ MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, cpu_type_(DynamicImages::GetNativeCPUType()), dyldImageLoadAddress_(NULL), dyldSlide_(0), - dyldPath_(), + dyldPath_(nullptr), task_context_(NULL), dynamic_images_(NULL), memory_blocks_(&allocator_) { @@ -250,7 +250,7 @@ void MinidumpGenerator::GatherCurrentProcessDyldInformation() { return; } dyldImageLoadAddress_ = mh; - dyldPath_ = string(aii->dyldPath); + dyldPath_ = aii->dyldPath; dyldSlide_ = GetCurrentProcessModuleSlide(mh, aii->sharedCacheSlide); } @@ -1464,7 +1464,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index, if (index == INT_MAX) { dyld_or_in_dyld_shared_cache = true; slide = dyldSlide_; - name = dyldPath_.c_str(); + name = dyldPath_; } else { dyld_or_in_dyld_shared_cache = ((header->flags & MH_SHAREDCACHE) != 0); @@ -1993,7 +1993,7 @@ bool MinidumpGenerator::WriteCrashInfoStream( bool dyld_or_in_dyld_shared_cache; if (i == image_count - 1) { slide = dyldSlide_; - module_path = dyldPath_.c_str(); + module_path = dyldPath_; dyld_or_in_dyld_shared_cache = true; } else { slide = _dyld_get_image_vmaddr_slide(i); @@ -2048,7 +2048,10 @@ bool MinidumpGenerator::WriteBootargsStream( int rv = sysctlbyname("kern.bootargs", NULL, &size, NULL, 0); if ((rv != 0) || (size == 0)) size = 1; - vector<uint8_t> bootargs(size); + + wasteful_vector<uint8_t> bootargs(&this->allocator_, size); + bootargs.resize(size, 0); + bootargs[0] = 0; if (rv == 0) sysctlbyname("kern.bootargs", &bootargs[0], &size, NULL, 0); diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h index aba067cc04..77c250ccd5 100644 --- a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h +++ b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h @@ -266,7 +266,8 @@ class MinidumpGenerator { // process has crashed. breakpad_mach_header* dyldImageLoadAddress_; ptrdiff_t dyldSlide_; - string dyldPath_; + // We don't own this pointer, this is owned by macOS internal structures. + const char* dyldPath_; // Context of the task to dump. breakpad_ucontext_t *task_context_; diff --git a/toolkit/crashreporter/docs/index.rst b/toolkit/crashreporter/docs/index.rst index f2444a75c6..fe1af45d8f 100644 --- a/toolkit/crashreporter/docs/index.rst +++ b/toolkit/crashreporter/docs/index.rst @@ -66,7 +66,8 @@ a ``google_breakpad::ExceptionHandler`` instance and it's stored as As the application runs, various other systems may write *annotations* or *notes* to the crash reporter to indicate state of the application, help with possible reasons for a current or future crash, etc. These are -performed via ``CrashReporter::AnnotateCrashReport()`` and +performed via ``CrashReporter::RecordAnnotation*()``, +``CrashReporter::RegisterAnnotation*()`` functions and ``CrashReporter::AppendAppNotesToCrashReport()`` from ``nsExceptionHandler.h``. @@ -154,7 +155,7 @@ with information about the crash. Submission of child process crashes is handled by application code. This code prompts the user to submit crashes in context-appropriate UI and then -submits the crashes using ``CrashSubmit.jsm``. +submits the crashes using ``CrashSubmit.sys.mjs``. Memory Reports ============== diff --git a/toolkit/crashreporter/linux_utils.cc b/toolkit/crashreporter/linux_utils.cc new file mode 100644 index 0000000000..cfddecf084 --- /dev/null +++ b/toolkit/crashreporter/linux_utils.cc @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "common/linux/linux_libc_support.h" +#include "linux_utils.h" + +bool ElfFileSoVersion(const char* mapping_name, uint32_t* version_components) { + if (!version_components) { + return false; + } + + // We found no version so just report 0 + const char* so_version = my_strstr(mapping_name, ".so."); + if (so_version == nullptr) { + return true; + } + + char tmp[12]; // 11 for maximum representation of UINT32T_MAX + \0 ? + size_t current_position = 0; + size_t next_tmp = 0; + tmp[0] = '\0'; + for (size_t so_version_pos = 0; so_version_pos <= my_strlen(so_version); + ++so_version_pos) { + // We can't have more than four: MAJOR.minor.release.patch + if (current_position == 4) { + break; + } + + char c = so_version[so_version_pos]; + if (c != '.') { + if ((c <= '9' && c >= '0')) { + tmp[next_tmp] = c; + tmp[next_tmp + 1] = '\0'; + ++next_tmp; + } + + if (so_version[so_version_pos + 1] != '\0') { + continue; + } + } + + if (my_strlen(tmp) > 0) { + int t; + if (!my_strtoui(&t, tmp)) { + return false; + } + uint32_t casted_tmp = (uint32_t)t; + version_components[current_position] = casted_tmp; + ++current_position; + } + + tmp[0] = '\0'; + next_tmp = 0; + } + + return true; +} diff --git a/toolkit/crashreporter/linux_utils.h b/toolkit/crashreporter/linux_utils.h new file mode 100644 index 0000000000..2dd4ff785c --- /dev/null +++ b/toolkit/crashreporter/linux_utils.h @@ -0,0 +1,16 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef toolkit_breakpad_linux_utils_h__ +#define toolkit_breakpad_linux_utils_h__ + +#include <stdio.h> +#include <stdint.h> +#include <string> +#include <vector> + +bool ElfFileSoVersion(const char* mapping_name, uint32_t* version); + +#endif /* toolkit_breakpad_linux_utils_h__ */ diff --git a/toolkit/crashreporter/moz.build b/toolkit/crashreporter/moz.build index d6f49680e7..952ca405bb 100644 --- a/toolkit/crashreporter/moz.build +++ b/toolkit/crashreporter/moz.build @@ -55,6 +55,14 @@ if CONFIG["MOZ_CRASHREPORTER"]: "google-breakpad/src/processor", ] + UNIFIED_SOURCES += [ + "linux_utils.cc", + ] + + EXPORTS += [ + "linux_utils.h", + ] + if CONFIG["MOZ_OXIDIZED_BREAKPAD"]: DIRS += ["rust_minidump_writer_linux"] diff --git a/toolkit/crashreporter/mozannotation_client/src/lib.rs b/toolkit/crashreporter/mozannotation_client/src/lib.rs index 17fe017095..df2bd5bf94 100644 --- a/toolkit/crashreporter/mozannotation_client/src/lib.rs +++ b/toolkit/crashreporter/mozannotation_client/src/lib.rs @@ -3,30 +3,53 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ use nsstring::nsCString; -use std::{ffi::c_void, sync::Mutex}; +use std::{ + alloc::{self, Layout}, + cmp::min, + ffi::{c_char, c_void, CStr}, + ptr::{copy_nonoverlapping, null_mut}, + sync::Mutex, +}; #[cfg(any(target_os = "linux", target_os = "android"))] use std::arch::global_asm; #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum AnnotationContents { Empty, - NSCString, + NSCStringPointer, + CStringPointer, CString, - CharBuffer, - USize, ByteBuffer(u32), + OwnedByteBuffer(u32), } - #[repr(C)] -#[derive(Clone, Copy)] pub struct Annotation { pub id: u32, pub contents: AnnotationContents, pub address: usize, } +impl Drop for Annotation { + fn drop(&mut self) { + match self.contents { + AnnotationContents::OwnedByteBuffer(len) => { + if (self.address != 0) && (len > 0) { + let align = min(usize::next_power_of_two(len as usize), 32); + unsafe { + let layout = Layout::from_size_align_unchecked(len as usize, align); + alloc::dealloc(self.address as *mut u8, layout); + } + } + } + _ => { + // Nothing to do + } + }; + } +} + pub struct AnnotationTable { data: Vec<Annotation>, magic_number: u32, @@ -222,98 +245,126 @@ pub struct MozAnnotationNote { pub ehdr: isize, } -/// Register an annotation containing an nsCString. -/// Returns false if the annotation is already present (and doesn't change it). +fn store_annotation<T>(id: u32, contents: AnnotationContents, address: *const T) -> *const T { + let address = match contents { + AnnotationContents::OwnedByteBuffer(len) => { + if !address.is_null() && (len > 0) { + // Copy the contents of this annotation, we'll own the copy + let len = len as usize; + let align = min(usize::next_power_of_two(len), 32); + unsafe { + let layout = Layout::from_size_align_unchecked(len as usize, align); + let src = address as *mut u8; + let dst = alloc::alloc(layout); + copy_nonoverlapping(src, dst, len); + dst + } + } else { + null_mut() + } + } + _ => address as *mut u8, + }; + + let annotations = &mut MOZANNOTATIONS.lock().unwrap().data; + let old = if let Some(existing) = annotations.iter_mut().find(|e| e.id == id) { + let old = match existing.contents { + AnnotationContents::OwnedByteBuffer(len) => { + // If we owned the previous value of this annotation we must free it. + if (existing.address != 0) && (len > 0) { + let len = len as usize; + let align = min(usize::next_power_of_two(len), 32); + unsafe { + let layout = Layout::from_size_align_unchecked(len, align); + alloc::dealloc(existing.address as *mut u8, layout); + } + } + null_mut::<T>() + } + _ => existing.address as *mut T, + }; + + existing.contents = contents; + existing.address = address as usize; + old + } else { + annotations.push(Annotation { + id, + contents, + address: address as usize, + }); + null_mut::<T>() + }; + + old +} + +/// Register a pointer to an nsCString string. +/// +/// Returns the value of the previously registered annotation or null. /// /// This function will be exposed to C++ #[no_mangle] -pub extern "C" fn mozannotation_register_nscstring(id: u32, address: *const nsCString) -> bool { - let annotations = &mut MOZANNOTATIONS.lock().unwrap().data; - - if annotations.iter().any(|e| e.id == id) { - return false; - } - - annotations.push(Annotation { - id, - contents: AnnotationContents::NSCString, - address: address as usize, - }); - - true +pub extern "C" fn mozannotation_register_nscstring( + id: u32, + address: *const nsCString, +) -> *const nsCString { + store_annotation(id, AnnotationContents::NSCStringPointer, address) } -/// Register an annotation containing a null-terminated string. -/// Returns false if the annotation is already present (and doesn't change it). +/// Create a copy of the provided string with a specified size that will be +/// owned by the crate, and register a pointer to it. /// /// This function will be exposed to C++ #[no_mangle] -pub extern "C" fn mozannotation_register_cstring( +pub extern "C" fn mozannotation_record_nscstring_from_raw_parts( id: u32, - address: *const *const std::ffi::c_char, -) -> bool { - let annotations = &mut MOZANNOTATIONS.lock().unwrap().data; - - if annotations.iter().any(|e| e.id == id) { - return false; - } - - annotations.push(Annotation { + address: *const u8, + size: usize, +) { + store_annotation( id, - contents: AnnotationContents::CString, - address: address as usize, - }); - - true + AnnotationContents::OwnedByteBuffer(size as u32), + address, + ); } -/// Register an annotation pointing to a buffer holding a null-terminated string. -/// Returns false if the annotation is already present (and doesn't change it). +/// Register a pointer to a pointer to a nul-terminated string. +/// +/// Returns the value of the previously registered annotation or null. /// /// This function will be exposed to C++ #[no_mangle] -pub extern "C" fn mozannotation_register_char_buffer( +pub extern "C" fn mozannotation_register_cstring_ptr( id: u32, - address: *const std::ffi::c_char, -) -> bool { - let annotations = &mut MOZANNOTATIONS.lock().unwrap().data; - - if annotations.iter().any(|e| e.id == id) { - return false; - } - - annotations.push(Annotation { - id, - contents: AnnotationContents::CharBuffer, - address: address as usize, - }); - - true + address: *const *const c_char, +) -> *const *const c_char { + store_annotation(id, AnnotationContents::CStringPointer, address) } -/// Register an annotation pointing to a variable of type usize. -/// Returns false if the annotation is already present (and doesn't change it). +/// Register a pointer to a nul-terminated string. +/// +/// Returns the value of the previously registered annotation or null. /// /// This function will be exposed to C++ #[no_mangle] -pub extern "C" fn mozannotation_register_usize(id: u32, address: *const usize) -> bool { - let annotations = &mut MOZANNOTATIONS.lock().unwrap().data; - - if annotations.iter().any(|e| e.id == id) { - return false; - } - - annotations.push(Annotation { - id, - contents: AnnotationContents::USize, - address: address as usize, - }); +pub extern "C" fn mozannotation_register_cstring(id: u32, address: *const c_char) -> *const c_char { + store_annotation(id, AnnotationContents::CString, address) +} - true +/// Create a copy of the provided nul-terminated string which will be owned by +/// the crate, and register a pointer to it. +/// +/// This function will be exposed to C++ +#[no_mangle] +pub extern "C" fn mozannotation_record_cstring(id: u32, address: *const c_char) { + let len = unsafe { CStr::from_ptr(address).to_bytes().len() }; + store_annotation(id, AnnotationContents::OwnedByteBuffer(len as u32), address); } -/// Register an annotation pointing to a fixed size buffer. -/// Returns false if the annotation is already present (and doesn't change it). +/// Register a pointer to a fixed size buffer. +/// +/// Returns the value of the previously registered annotation or null. /// /// This function will be exposed to C++ #[no_mangle] @@ -321,34 +372,50 @@ pub extern "C" fn mozannotation_register_bytebuffer( id: u32, address: *const c_void, size: u32, -) -> bool { - let annotations = &mut MOZANNOTATIONS.lock().unwrap().data; - - if annotations.iter().any(|e| e.id == id) { - return false; - } +) -> *const c_void { + store_annotation(id, AnnotationContents::ByteBuffer(size), address) +} - annotations.push(Annotation { - id, - contents: AnnotationContents::ByteBuffer(size), - address: address as usize, - }); +/// Create a copy of the provided buffer which will be owned by the crate, and +/// register a pointer to it. +/// +/// This function will be exposed to C++ +#[no_mangle] +pub extern "C" fn mozannotation_record_bytebuffer(id: u32, address: *const c_void, size: u32) { + store_annotation(id, AnnotationContents::OwnedByteBuffer(size), address); +} - true +/// Unregister a crash annotation. Returns the previously registered pointer or +/// null if none was present. Return null also if the crate owned the +/// annotations' buffer. +/// +/// This function will be exposed to C++ +#[no_mangle] +pub extern "C" fn mozannotation_unregister(id: u32) -> *const c_void { + store_annotation(id, AnnotationContents::Empty, null_mut()) } -/// Unregister a crash annotation. Returns false if the annotation is not present. +/// Returns the raw address of an annotation if it has been registered or NULL +/// if it hasn't. /// /// This function will be exposed to C++ #[no_mangle] -pub extern "C" fn mozannotation_unregister(id: u32) -> bool { - let annotations = &mut MOZANNOTATIONS.lock().unwrap().data; - let index = annotations.iter().position(|e| e.id == id); +pub extern "C" fn mozannotation_get_contents(id: u32, contents: *mut AnnotationContents) -> usize { + let annotations = &MOZANNOTATIONS.lock().unwrap().data; + if let Some(annotation) = annotations.iter().find(|e| e.id == id) { + if annotation.contents == AnnotationContents::Empty { + return 0; + } - if let Some(index) = index { - annotations.remove(index); - return true; + unsafe { *contents = annotation.contents }; + return annotation.address; } - false + return 0; +} + +#[no_mangle] +pub extern "C" fn mozannotation_clear_all() { + let annotations = &mut MOZANNOTATIONS.lock().unwrap().data; + annotations.clear(); } diff --git a/toolkit/crashreporter/mozannotation_server/Cargo.toml b/toolkit/crashreporter/mozannotation_server/Cargo.toml index 747aff791e..3a5834a645 100644 --- a/toolkit/crashreporter/mozannotation_server/Cargo.toml +++ b/toolkit/crashreporter/mozannotation_server/Cargo.toml @@ -11,7 +11,6 @@ license = "MPL-2.0" goblin = { version = "0.7", features = ["elf32", "elf64", "pe32", "pe64"] } memoffset = "0.8" mozannotation_client = { path = "../mozannotation_client/" } -nsstring = { path = "../../../xpcom/rust/nsstring/" } thin-vec = { version = "0.2.7", features = ["gecko-ffi"] } thiserror = "1.0.38" diff --git a/toolkit/crashreporter/mozannotation_server/src/lib.rs b/toolkit/crashreporter/mozannotation_server/src/lib.rs index 4ecb8ec919..6ed5b3cc41 100644 --- a/toolkit/crashreporter/mozannotation_server/src/lib.rs +++ b/toolkit/crashreporter/mozannotation_server/src/lib.rs @@ -9,22 +9,21 @@ use crate::errors::*; use process_reader::ProcessReader; use mozannotation_client::{Annotation, AnnotationContents, AnnotationMutex}; -use nsstring::nsCString; use std::cmp::min; use std::iter::FromIterator; -use std::mem::size_of; +use std::mem::{size_of, ManuallyDrop}; use std::ptr::null_mut; use thin_vec::ThinVec; #[repr(C)] +#[derive(Debug)] pub enum AnnotationData { Empty, - UsizeData(usize), - NSCStringData(nsCString), ByteBuffer(ThinVec<u8>), } #[repr(C)] +#[derive(Debug)] pub struct CAnnotation { id: u32, data: AnnotationData, @@ -100,33 +99,33 @@ pub fn retrieve_annotations( // Read an annotation from the given address fn read_annotation(reader: &ProcessReader, address: usize) -> Result<CAnnotation, ReadError> { - let raw_annotation = reader.copy_object::<Annotation>(address)?; + let raw_annotation = ManuallyDrop::new(reader.copy_object::<Annotation>(address)?); let mut annotation = CAnnotation { id: raw_annotation.id, data: AnnotationData::Empty, }; + if raw_annotation.address == 0 { + return Ok(annotation); + } + match raw_annotation.contents { AnnotationContents::Empty => {} - AnnotationContents::NSCString => { + AnnotationContents::NSCStringPointer => { let string = copy_nscstring(reader, raw_annotation.address)?; - annotation.data = AnnotationData::NSCStringData(string); - } - AnnotationContents::CString => { - let string = copy_null_terminated_string_pointer(reader, raw_annotation.address)?; - annotation.data = AnnotationData::NSCStringData(string); + annotation.data = AnnotationData::ByteBuffer(string); } - AnnotationContents::CharBuffer => { - let string = copy_null_terminated_string(reader, raw_annotation.address)?; - annotation.data = AnnotationData::NSCStringData(string); + AnnotationContents::CStringPointer => { + let buffer = copy_null_terminated_string_pointer(reader, raw_annotation.address)?; + annotation.data = AnnotationData::ByteBuffer(buffer); } - AnnotationContents::USize => { - let value = reader.copy_object::<usize>(raw_annotation.address)?; - annotation.data = AnnotationData::UsizeData(value); + AnnotationContents::CString => { + let buffer = copy_null_terminated_string(reader, raw_annotation.address)?; + annotation.data = AnnotationData::ByteBuffer(buffer); } - AnnotationContents::ByteBuffer(size) => { - let value = copy_bytebuffer(reader, raw_annotation.address, size)?; - annotation.data = AnnotationData::ByteBuffer(value); + AnnotationContents::ByteBuffer(size) | AnnotationContents::OwnedByteBuffer(size) => { + let buffer = copy_bytebuffer(reader, raw_annotation.address, size)?; + annotation.data = AnnotationData::ByteBuffer(buffer); } }; @@ -136,7 +135,7 @@ fn read_annotation(reader: &ProcessReader, address: usize) -> Result<CAnnotation fn copy_null_terminated_string_pointer( reader: &ProcessReader, address: usize, -) -> Result<nsCString, ReadError> { +) -> Result<ThinVec<u8>, ReadError> { let buffer_address = reader.copy_object::<usize>(address)?; copy_null_terminated_string(reader, buffer_address) } @@ -144,7 +143,7 @@ fn copy_null_terminated_string_pointer( fn copy_null_terminated_string( reader: &ProcessReader, address: usize, -) -> Result<nsCString, ReadError> { +) -> Result<ThinVec<u8>, ReadError> { // Try copying the string word-by-word first, this is considerably faster // than one byte at a time. if let Ok(string) = copy_null_terminated_string_word_by_word(reader, address) { @@ -155,7 +154,7 @@ fn copy_null_terminated_string( // at a time. It's slow but it might work in situations where the string // alignment causes word-by-word access to straddle page boundaries. let mut length = 0; - let mut string = Vec::<u8>::new(); + let mut string = ThinVec::<u8>::new(); loop { let char = reader.copy_object::<u8>(address + length)?; @@ -167,33 +166,34 @@ fn copy_null_terminated_string( } } - Ok(nsCString::from(&string[..length])) + Ok(string) } fn copy_null_terminated_string_word_by_word( reader: &ProcessReader, address: usize, -) -> Result<nsCString, ReadError> { +) -> Result<ThinVec<u8>, ReadError> { const WORD_SIZE: usize = size_of::<usize>(); let mut length = 0; - let mut string = Vec::<u8>::new(); + let mut string = ThinVec::<u8>::new(); loop { - let mut array = reader.copy_array::<u8>(address + length, WORD_SIZE)?; + let array = reader.copy_array::<u8>(address + length, WORD_SIZE)?; let null_terminator = array.iter().position(|&e| e == 0); length += null_terminator.unwrap_or(WORD_SIZE); - string.append(&mut array); + string.extend(array.into_iter()); if null_terminator.is_some() { + string.truncate(length); break; } } - Ok(nsCString::from(&string[..length])) + Ok(string) } -fn copy_nscstring(reader: &ProcessReader, address: usize) -> Result<nsCString, ReadError> { - // HACK: This assumes the layout of the string +fn copy_nscstring(reader: &ProcessReader, address: usize) -> Result<ThinVec<u8>, ReadError> { + // HACK: This assumes the layout of the nsCString object let length_address = address + size_of::<usize>(); let length = reader.copy_object::<u32>(length_address)?; @@ -201,9 +201,9 @@ fn copy_nscstring(reader: &ProcessReader, address: usize) -> Result<nsCString, R let data_address = reader.copy_object::<usize>(address)?; reader .copy_array::<u8>(data_address, length as _) - .map(nsCString::from) + .map(ThinVec::from) } else { - Ok(nsCString::new()) + Ok(ThinVec::<u8>::new()) } } diff --git a/toolkit/crashreporter/mozannotation_server/src/process_reader/linux.rs b/toolkit/crashreporter/mozannotation_server/src/process_reader/linux.rs index db6dbd3df2..34f8ef90d5 100644 --- a/toolkit/crashreporter/mozannotation_server/src/process_reader/linux.rs +++ b/toolkit/crashreporter/mozannotation_server/src/process_reader/linux.rs @@ -261,8 +261,10 @@ enum PTraceOperation { PeekData, } -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", target_env = "gnu"))] type PTraceOperationNative = libc::c_uint; +#[cfg(all(target_os = "linux", target_env = "musl"))] +type PTraceOperationNative = libc::c_int; #[cfg(target_os = "android")] type PTraceOperationNative = c_int; diff --git a/toolkit/crashreporter/nsDummyExceptionHandler.cpp b/toolkit/crashreporter/nsDummyExceptionHandler.cpp index 7ccede695f..cd76630f3c 100644 --- a/toolkit/crashreporter/nsDummyExceptionHandler.cpp +++ b/toolkit/crashreporter/nsDummyExceptionHandler.cpp @@ -4,10 +4,7 @@ * 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/. */ -#include <functional> - #include "nsExceptionHandler.h" -#include "nsExceptionHandlerUtils.h" namespace CrashReporter { @@ -38,41 +35,71 @@ nsresult SetupExtraData(nsIFile* aAppDataDirectory, nsresult UnsetExceptionHandler() { return NS_ERROR_NOT_IMPLEMENTED; } -nsresult AnnotateCrashReport(Annotation key, bool data) { - return NS_ERROR_NOT_IMPLEMENTED; +const bool* RegisterAnnotationBool(Annotation aKey, const bool* aData) { + return nullptr; } -nsresult AnnotateCrashReport(Annotation key, int data) { - return NS_ERROR_NOT_IMPLEMENTED; +const uint32_t* RegisterAnnotationU32(Annotation aKey, const uint32_t* aData) { + return nullptr; } -nsresult AnnotateCrashReport(Annotation key, unsigned int data) { - return NS_ERROR_NOT_IMPLEMENTED; +const uint64_t* RegisterAnnotationU64(Annotation aKey, const uint64_t* aData) { + return nullptr; } -nsresult AnnotateCrashReport(Annotation key, const nsACString& data) { - return NS_ERROR_NOT_IMPLEMENTED; +const size_t* RegisterAnnotationUSize(Annotation aKey, const size_t* aData) { + return nullptr; } -nsresult AppendToCrashReportAnnotation(Annotation key, const nsACString& data) { - return NS_ERROR_NOT_IMPLEMENTED; +const char* RegisterAnnotationCString(Annotation aKey, const char* aData) { + return nullptr; } -nsresult RemoveCrashReportAnnotation(Annotation key) { - return NS_ERROR_NOT_IMPLEMENTED; +const nsCString* RegisterAnnotationNSCString(Annotation aKey, + const nsCString* aData) { + return nullptr; +} + +nsresult RecordAnnotationBool(Annotation aKey, bool aData) { + return NS_ERROR_FAILURE; +} + +nsresult RecordAnnotationU32(Annotation aKey, uint32_t aData) { + return NS_ERROR_FAILURE; } -AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key, bool data) {} +nsresult RecordAnnotationU64(Annotation aKey, uint64_t aData) { + return NS_ERROR_FAILURE; +} + +nsresult RecordAnnotationUSize(Annotation aKey, size_t aData) { + return NS_ERROR_FAILURE; +} + +nsresult RecordAnnotationCString(Annotation aKey, const char* aData) { + return NS_ERROR_FAILURE; +} + +nsresult RecordAnnotationNSCString(Annotation aKey, const nsACString& aData) { + return NS_ERROR_FAILURE; +} + +nsresult RecordAnnotationNSString(Annotation aKey, const nsAString& aData) { + return NS_ERROR_FAILURE; +} + +nsresult UnrecordAnnotation(Annotation aKey) { return NS_ERROR_FAILURE; } + +AutoRecordAnnotation::AutoRecordAnnotation(Annotation key, bool data) {} -AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key, int data) {} +AutoRecordAnnotation::AutoRecordAnnotation(Annotation key, int data) {} -AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key, - unsigned data) {} +AutoRecordAnnotation::AutoRecordAnnotation(Annotation key, unsigned data) {} -AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key, - const nsACString& data) {} +AutoRecordAnnotation::AutoRecordAnnotation(Annotation key, + const nsACString& data) {} -AutoAnnotateCrashReport::~AutoAnnotateCrashReport() {} +AutoRecordAnnotation::~AutoRecordAnnotation() {} void MergeCrashAnnotations(AnnotationTable& aDst, const AnnotationTable& aSrc) { } diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp index ceb021f7f3..aca266749a 100644 --- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -201,6 +201,7 @@ static const char kCrashMainID[] = "crash.main.3\n"; static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr; static mozilla::Atomic<bool> gEncounteredChildException(false); +static nsCString gServerURL; static xpstring pendingDirectory; static xpstring crashReporterPath; @@ -232,9 +233,7 @@ static const char* androidStartServiceCommand = nullptr; #endif // this holds additional data sent via the API -static Mutex* crashReporterAPILock; static Mutex* notesFieldLock; -static AnnotationTable crashReporterAPIData_Table; static nsCString* notesField = nullptr; static bool isGarbageCollecting; static uint32_t eventloopNestingLevel = 0; @@ -656,11 +655,17 @@ class JSONAnnotationWriter : public AnnotationWriter { size_t len = aLen ? aLen : my_strlen(aValue); const char* annotationStr = AnnotationToString(aAnnotation); - WritePrefix(); - mWriter.WriteBuffer(annotationStr, my_strlen(annotationStr)); - WriteSeparator(); - WriteEscapedString(aValue, len); - WriteSuffix(); + if (len && CrashReporter::ShouldIncludeAnnotation(aAnnotation, aValue)) { + WritePrefix(); + mWriter.WriteBuffer(annotationStr, my_strlen(annotationStr)); + WriteSeparator(); + WriteEscapedString(aValue, len); + WriteSuffix(); + } + }; + + void Write(Annotation aAnnotation, bool aValue) override { + Write(aAnnotation, aValue ? "1" : "0", 1); }; void Write(Annotation aAnnotation, uint64_t aValue) override { @@ -789,8 +794,10 @@ static void WritePHCAddrInfo(AnnotationWriter& writer, break; } writer.Write(Annotation::PHCKind, kindString); - writer.Write(Annotation::PHCBaseAddress, uintptr_t(aAddrInfo->mBaseAddr)); - writer.Write(Annotation::PHCUsableSize, aAddrInfo->mUsableSize); + writer.Write(Annotation::PHCBaseAddress, + reinterpret_cast<uint64_t>(aAddrInfo->mBaseAddr)); + writer.Write(Annotation::PHCUsableSize, + static_cast<uint64_t>(aAddrInfo->mUsableSize)); WritePHCStackTrace(writer, Annotation::PHCAllocStack, aAddrInfo->mAllocStack); @@ -879,7 +886,8 @@ static void AnnotateMemoryStatus(AnnotationWriter& aWriter) { MEMORYSTATUSEX statex; statex.dwLength = sizeof(statex); if (GlobalMemoryStatusEx(&statex)) { - aWriter.Write(Annotation::SystemMemoryUsePercentage, statex.dwMemoryLoad); + aWriter.Write(Annotation::SystemMemoryUsePercentage, + static_cast<uint64_t>(statex.dwMemoryLoad)); aWriter.Write(Annotation::TotalVirtualMemory, statex.ullTotalVirtual); aWriter.Write(Annotation::AvailableVirtualMemory, statex.ullAvailVirtual); aWriter.Write(Annotation::TotalPhysicalMemory, statex.ullTotalPhys); @@ -888,9 +896,11 @@ static void AnnotateMemoryStatus(AnnotationWriter& aWriter) { PERFORMANCE_INFORMATION info; if (K32GetPerformanceInfo(&info, sizeof(info))) { - aWriter.Write(Annotation::TotalPageFile, info.CommitLimit * info.PageSize); + aWriter.Write(Annotation::TotalPageFile, + static_cast<uint64_t>(info.CommitLimit * info.PageSize)); aWriter.Write(Annotation::AvailablePageFile, - (info.CommitLimit - info.CommitTotal) * info.PageSize); + static_cast<uint64_t>((info.CommitLimit - info.CommitTotal) * + info.PageSize)); } } #elif XP_MACOSX @@ -916,9 +926,9 @@ static void WriteAvailableMemoryStatus(AnnotationWriter& aWriter) { if (host_statistics64(host, HOST_VM_INFO64, (host_info64_t)&stats, &count) == KERN_SUCCESS) { aWriter.Write(Annotation::AvailablePhysicalMemory, - stats.free_count * vm_page_size); + static_cast<uint64_t>(stats.free_count * vm_page_size)); aWriter.Write(Annotation::PurgeablePhysicalMemory, - stats.purgeable_count * vm_page_size); + static_cast<uint64_t>(stats.purgeable_count * vm_page_size)); } } @@ -1199,7 +1209,8 @@ static void AnnotateMemoryStatus(AnnotationWriter& aWriter) { pointOfInterest.dest->value = value; } if (pointOfInterest.annotation != Annotation::Count) { - aWriter.Write(pointOfInterest.annotation, value); + aWriter.Write(pointOfInterest.annotation, + static_cast<uint64_t>(value)); } } break; @@ -1224,7 +1235,8 @@ static void AnnotateMemoryStatus(AnnotationWriter& aWriter) { } if (memTotal.found && swapTotal.found) { // If available, attempt to determine the available virtual memory. - aWriter.Write(Annotation::TotalPageFile, memTotal.value + swapTotal.value); + aWriter.Write(Annotation::TotalPageFile, + static_cast<uint64_t>(memTotal.value + swapTotal.value)); } } @@ -1356,40 +1368,6 @@ static bool LaunchCrashHandlerService(const XP_CHAR* aProgramPath, #endif -static void WriteMainThreadRunnableName(AnnotationWriter& aWriter) { -#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY - // Only try to collect this information if the main thread is crashing. - if (!SignalSafeIsMainThread()) { - return; - } - - // NOTE: Use `my_memchr` over `strlen` to ensure we don't run off the end of - // the buffer if it contains no null bytes. This is used instead of `strnlen`, - // as breakpad's linux support library doesn't export a `my_strnlen` function. - const char* buf = nsThread::sMainThreadRunnableName.begin(); - size_t len = nsThread::kRunnableNameBufSize; - if (const void* end = my_memchr(buf, '\0', len)) { - len = static_cast<const char*>(end) - buf; - } - - if (len > 0) { - aWriter.Write(Annotation::MainThreadRunnableName, buf, len); - } -#endif -} - -static void WriteOOMAllocationSize(AnnotationWriter& aWriter) { - if (gOOMAllocationSize) { - aWriter.Write(Annotation::OOMAllocationSize, gOOMAllocationSize); - } -} - -static void WriteMozCrashReason(AnnotationWriter& aWriter) { - if (gMozCrashReason != nullptr) { - aWriter.Write(Annotation::MozCrashReason, gMozCrashReason); - } -} - static void WriteAnnotations(AnnotationWriter& aWriter, const AnnotationTable& aAnnotations) { for (auto key : MakeEnumeratedRange(Annotation::Count)) { @@ -1408,13 +1386,65 @@ static void WriteAnnotationsForMainProcessCrash(PlatformWriter& pw, const phc::AddrInfo* addrInfo, time_t crashTime) { JSONAnnotationWriter writer(pw); - WriteAnnotations(writer, crashReporterAPIData_Table); + + for (auto key : MakeEnumeratedRange(Annotation::Count)) { + AnnotationContents contents = {}; + size_t address = + mozannotation_get_contents(static_cast<uint32_t>(key), &contents); + if (address != 0) { + switch (TypeOfAnnotation(key)) { + case AnnotationType::String: + switch (contents.tag) { + case AnnotationContents::Tag::NSCStringPointer: { + const nsCString* string = + reinterpret_cast<const nsCString*>(address); + writer.Write(key, string->Data(), string->Length()); + } break; + case AnnotationContents::Tag::CStringPointer: + address = *(reinterpret_cast<size_t*>(address)); + if (address == 0) { + break; + } + // FALLTHROUGH + case AnnotationContents::Tag::CString: { + writer.Write(key, reinterpret_cast<const char*>(address)); + } break; + case AnnotationContents::Tag::ByteBuffer: + writer.Write(key, reinterpret_cast<const char*>(address), + static_cast<size_t>(contents.byte_buffer._0)); + break; + case AnnotationContents::Tag::OwnedByteBuffer: + writer.Write(key, reinterpret_cast<const char*>(address), + static_cast<size_t>(contents.owned_byte_buffer._0)); + break; + case AnnotationContents::Tag::Empty: + break; + } + break; + case AnnotationType::Boolean: + writer.Write(key, *reinterpret_cast<const bool*>(address)); + break; + case AnnotationType::U32: + writer.Write(key, static_cast<uint64_t>( + *reinterpret_cast<uint32_t*>(address))); + break; + case AnnotationType::U64: + writer.Write(key, *reinterpret_cast<uint64_t*>(address)); + break; + case AnnotationType::USize: + writer.Write( + key, static_cast<uint64_t>(*reinterpret_cast<size_t*>(address))); + break; + } + } + } + WriteSynthesizedAnnotations(writer); writer.Write(Annotation::CrashTime, uint64_t(crashTime)); if (inactiveStateStart) { writer.Write(Annotation::LastInteractionDuration, - crashTime - inactiveStateStart); + static_cast<uint64_t>(crashTime - inactiveStateStart)); } double uptimeTS = (TimeStamp::NowLoRes() - TimeStamp::ProcessCreation()) @@ -1427,34 +1457,14 @@ static void WriteAnnotationsForMainProcessCrash(PlatformWriter& pw, if (lastCrashTime != 0) { uint64_t timeSinceLastCrash = crashTime - lastCrashTime; - if (timeSinceLastCrash != 0) { - writer.Write(Annotation::SecondsSinceLastCrash, timeSinceLastCrash); - } - } - - if (isGarbageCollecting) { - writer.Write(Annotation::IsGarbageCollecting, "1"); - } - - if (eventloopNestingLevel > 0) { - writer.Write(Annotation::EventLoopNestingLevel, eventloopNestingLevel); + writer.Write(Annotation::SecondsSinceLastCrash, timeSinceLastCrash); } #if defined(XP_WIN) && defined(HAS_DLL_BLOCKLIST) // HACK: The DLL blocklist code will manually write its annotations as JSON - DllBlocklist_WriteNotes(writer); + DllBlocklist_WriteNotes(); #endif // defined(XP_WIN) && defined(HAS_DLL_BLOCKLIST) - WriteMozCrashReason(writer); - - WriteMainThreadRunnableName(writer); - - WriteOOMAllocationSize(writer); - - if (gTexturesSize) { - writer.Write(Annotation::TextureUsage, gTexturesSize); - } - #ifdef MOZ_PHC WritePHCAddrInfo(writer, addrInfo); #endif @@ -1889,19 +1899,50 @@ static nsresult LocateExecutable(nsIFile* aXREDirectory, const nsAString& aName, #endif // !defined(MOZ_WIDGET_ANDROID) -static void InitializeAnnotationFacilities() { - crashReporterAPILock = new Mutex("crashReporterAPILock"); +static void InitializeAppNotes() { notesFieldLock = new Mutex("notesFieldLock"); notesField = new nsCString(); } -static void TeardownAnnotationFacilities() { - std::fill(crashReporterAPIData_Table.begin(), - crashReporterAPIData_Table.end(), ""_ns); - - delete crashReporterAPILock; - crashReporterAPILock = nullptr; +// Register crash annotations that are present in both main and child processes +static void RegisterAnnotations() { + mozannotation_register_cstring_ptr( + static_cast<uint32_t>(Annotation::MozCrashReason), &gMozCrashReason); +#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY + mozannotation_register_cstring( + static_cast<uint32_t>(Annotation::MainThreadRunnableName), + &nsThread::sMainThreadRunnableName[0]); +#endif + mozannotation_register_bytebuffer( + static_cast<uint32_t>(Annotation::EventLoopNestingLevel), + &eventloopNestingLevel, sizeof(uint32_t)); + mozannotation_register_nscstring(static_cast<uint32_t>(Annotation::Notes), + notesField); + mozannotation_register_bytebuffer( + static_cast<uint32_t>(Annotation::OOMAllocationSize), &gOOMAllocationSize, + sizeof(size_t)); + mozannotation_register_bytebuffer( + static_cast<uint32_t>(Annotation::IsGarbageCollecting), + &isGarbageCollecting, sizeof(bool)); + mozannotation_register_nscstring(static_cast<uint32_t>(Annotation::ServerURL), + &gServerURL); + mozannotation_register_bytebuffer( + static_cast<uint32_t>(Annotation::TextureUsage), &gTexturesSize, + sizeof(size_t)); +#if defined(XP_WIN) && defined(HAS_DLL_BLOCKLIST) + mozannotation_register_bytebuffer( + static_cast<uint32_t>(Annotation::BlocklistInitFailed), + DllBlocklist_GetBlocklistInitFailedPointer(), sizeof(bool)); + mozannotation_register_bytebuffer( + static_cast<uint32_t>(Annotation::User32BeforeBlocklist), + DllBlocklist_GetUser32BeforeBlocklistPointer(), sizeof(bool)); + mozannotation_register_cstring( + static_cast<uint32_t>(Annotation::BlockedDllList), + DllBlocklist_GetBlocklistWriterData()); +#endif // defined(XP_WIN) && defined(HAS_DLL_BLOCKLIST) +} +static void TeardownAppNotes() { delete notesFieldLock; notesFieldLock = nullptr; @@ -1929,7 +1970,8 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory, bool force /*=false*/) { doReport = ShouldReport(); RegisterRuntimeExceptionModule(); - InitializeAnnotationFacilities(); + InitializeAppNotes(); + RegisterAnnotations(); #if !defined(MOZ_WIDGET_ANDROID) // Locate the crash reporter executable @@ -2055,10 +2097,8 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory, bool force /*=false*/) { #endif // store application start time - char timeString[32]; - time_t startupTime = time(nullptr); - XP_TTOA(startupTime, timeString); - AnnotateCrashReport(Annotation::StartupTime, nsDependentCString(timeString)); + RecordAnnotationU64(Annotation::StartupTime, + static_cast<uint64_t>(time(nullptr))); #if defined(XP_MACOSX) // On OS X, many testers like to see the OS crash reporting dialog @@ -2259,8 +2299,9 @@ nsresult SetupExtraData(nsIFile* aAppDataDirectory, nsAutoCString data; if (NS_SUCCEEDED(GetOrInit(dataDirectory, "InstallTime"_ns + aBuildID, data, - InitInstallTime))) - AnnotateCrashReport(Annotation::InstallTime, data); + InitInstallTime))) { + RecordAnnotationNSCString(Annotation::InstallTime, data); + } // this is a little different, since we can't init it with anything, // since it's stored at crash time, and we can't annotate the @@ -2314,7 +2355,8 @@ nsresult UnsetExceptionHandler() { delete gExceptionHandler; - TeardownAnnotationFacilities(); + gServerURL = ""; + TeardownAppNotes(); if (!gExceptionHandler) return NS_ERROR_NOT_INITIALIZED; @@ -2330,73 +2372,201 @@ nsresult UnsetExceptionHandler() { return NS_OK; } -nsresult AnnotateCrashReport(Annotation key, bool data) { - return AnnotateCrashReport(key, data ? "1"_ns : "0"_ns); +const bool* RegisterAnnotationBool(Annotation aKey, const bool* aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::Boolean, + "Wrong annotation type"); + + if (!GetEnabled()) { + return nullptr; + } + + return reinterpret_cast<const bool*>(mozannotation_register_bytebuffer( + static_cast<uint32_t>(aKey), aData, sizeof(bool))); } -nsresult AnnotateCrashReport(Annotation key, int data) { - nsAutoCString dataString; - dataString.AppendInt(data); +const uint32_t* RegisterAnnotationU32(Annotation aKey, const uint32_t* aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::U32, + "Wrong annotation type"); - return AnnotateCrashReport(key, dataString); + if (!GetEnabled()) { + return nullptr; + } + + return reinterpret_cast<const uint32_t*>(mozannotation_register_bytebuffer( + static_cast<uint32_t>(aKey), aData, sizeof(uint32_t))); } -nsresult AnnotateCrashReport(Annotation key, unsigned int data) { - nsAutoCString dataString; - dataString.AppendInt(data); +const uint64_t* RegisterAnnotationU64(Annotation aKey, const uint64_t* aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::U64, + "Wrong annotation type"); - return AnnotateCrashReport(key, dataString); + if (!GetEnabled()) { + return nullptr; + } + + return reinterpret_cast<const uint64_t*>(mozannotation_register_bytebuffer( + static_cast<uint32_t>(aKey), aData, sizeof(uint64_t))); } -nsresult AnnotateCrashReport(Annotation key, const nsACString& data) { - if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED; +const size_t* RegisterAnnotationUSize(Annotation aKey, const size_t* aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::USize, + "Wrong annotation type"); + + if (!GetEnabled()) { + return nullptr; + } + + return reinterpret_cast<const size_t*>(mozannotation_register_bytebuffer( + static_cast<uint32_t>(aKey), aData, sizeof(size_t))); +} + +const char* RegisterAnnotationCString(Annotation aKey, const char* aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::String, + "Wrong annotation type"); - MutexAutoLock lock(*crashReporterAPILock); - crashReporterAPIData_Table[key] = data; + if (!GetEnabled()) { + return nullptr; + } + return mozannotation_register_cstring(static_cast<uint32_t>(aKey), aData); +} + +const nsCString* RegisterAnnotationNSCString(Annotation aKey, + const nsCString* aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::String, + "Wrong annotation type"); + + if (!GetEnabled()) { + return nullptr; + } + + return mozannotation_register_nscstring(static_cast<uint32_t>(aKey), aData); +} + +nsresult RecordAnnotationBool(Annotation aKey, bool aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::Boolean, + "Wrong annotation type"); + + if (!GetEnabled()) { + return NS_ERROR_NOT_INITIALIZED; + } + + mozannotation_record_bytebuffer(static_cast<uint32_t>(aKey), &aData, + sizeof(bool)); return NS_OK; } -nsresult AppendToCrashReportAnnotation(Annotation key, const nsACString& data) { - if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED; +nsresult RecordAnnotationU32(Annotation aKey, uint32_t aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::U32, + "Wrong annotation type"); - MutexAutoLock lock(*crashReporterAPILock); - nsAutoCString newString(crashReporterAPIData_Table[key]); - newString.Append(" - "_ns); - newString.Append(data); - crashReporterAPIData_Table[key] = newString; + if (!GetEnabled()) { + return NS_ERROR_NOT_INITIALIZED; + } + mozannotation_record_bytebuffer(static_cast<uint32_t>(aKey), &aData, + sizeof(uint32_t)); return NS_OK; } -nsresult RemoveCrashReportAnnotation(Annotation key) { - return AnnotateCrashReport(key, ""_ns); +nsresult RecordAnnotationU64(Annotation aKey, uint64_t aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::U64, + "Wrong annotation type"); + + if (!GetEnabled()) { + return NS_ERROR_NOT_INITIALIZED; + } + + mozannotation_record_bytebuffer(static_cast<uint64_t>(aKey), &aData, + sizeof(uint64_t)); + return NS_OK; } -AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key, bool data) - : AutoAnnotateCrashReport(key, data ? "1"_ns : "0"_ns) {} +nsresult RecordAnnotationUSize(Annotation aKey, size_t aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::USize, + "Wrong annotation type"); -AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key, int data) - : AutoAnnotateCrashReport(key, nsPrintfCString("%d", data)) {} + if (!GetEnabled()) { + return NS_ERROR_NOT_INITIALIZED; + } -AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key, unsigned data) - : AutoAnnotateCrashReport(key, nsPrintfCString("%u", data)) {} + mozannotation_record_bytebuffer(static_cast<size_t>(aKey), &aData, + sizeof(size_t)); + return NS_OK; +} -AutoAnnotateCrashReport::AutoAnnotateCrashReport(Annotation key, - const nsACString& data) - : mKey(key) { +nsresult RecordAnnotationCString(Annotation aKey, const char* aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::String, + "Wrong annotation type"); + + if (!GetEnabled()) { + return NS_ERROR_NOT_INITIALIZED; + } + mozannotation_record_cstring(static_cast<uint32_t>(aKey), aData); + return NS_OK; +} + +nsresult RecordAnnotationNSCString(Annotation aKey, const nsACString& aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::String, + "Wrong annotation type"); + + if (!GetEnabled()) { + return NS_ERROR_NOT_INITIALIZED; + } + + mozannotation_record_nscstring_from_raw_parts( + static_cast<uint32_t>(aKey), + reinterpret_cast<const uint8_t*>(aData.Data()), aData.Length()); + return NS_OK; +} + +nsresult RecordAnnotationNSString(Annotation aKey, const nsAString& aData) { + MOZ_ASSERT(TypeOfAnnotation(aKey) == AnnotationType::String, + "Wrong annotation type"); + + if (!GetEnabled()) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsAutoCString data = NS_ConvertUTF16toUTF8(aData); + RecordAnnotationNSCString(aKey, data); + return NS_OK; +} + +nsresult UnrecordAnnotation(Annotation aKey) { + if (!GetEnabled()) { + return NS_ERROR_NOT_INITIALIZED; + } + + mozannotation_unregister(static_cast<uint32_t>(aKey)); + return NS_OK; +} + +// TODO: The first three methods here should be migrated to use native +// types instead of turning the values into strings. They're not currently +// used in mozilla-central so it doesn't really matter in the short term. +AutoRecordAnnotation::AutoRecordAnnotation(Annotation key, bool data) + : AutoRecordAnnotation(key, data ? "1"_ns : "0"_ns) {} + +AutoRecordAnnotation::AutoRecordAnnotation(Annotation key, int data) + : AutoRecordAnnotation(key, nsPrintfCString("%d", data)) {} + +AutoRecordAnnotation::AutoRecordAnnotation(Annotation key, unsigned data) + : AutoRecordAnnotation(key, nsPrintfCString("%u", data)) {} + +AutoRecordAnnotation::AutoRecordAnnotation(Annotation key, + const nsACString& data) + : mKey(key), mCurrent(data) { if (GetEnabled()) { - MutexAutoLock lock(*crashReporterAPILock); - auto& entry = crashReporterAPIData_Table[mKey]; - mPrevious = std::move(entry); - entry = data; + mPrevious = + mozannotation_register_nscstring(static_cast<uint32_t>(key), &mCurrent); } } -AutoAnnotateCrashReport::~AutoAnnotateCrashReport() { +AutoRecordAnnotation::~AutoRecordAnnotation() { if (GetEnabled()) { - MutexAutoLock lock(*crashReporterAPILock); - crashReporterAPIData_Table[mKey] = std::move(mPrevious); + Unused << mozannotation_register_nscstring(static_cast<uint32_t>(mKey), + mPrevious); } } @@ -2409,11 +2579,6 @@ void MergeCrashAnnotations(AnnotationTable& aDst, const AnnotationTable& aSrc) { } } -static void MergeContentCrashAnnotations(AnnotationTable& aDst) { - MutexAutoLock lock(*crashReporterAPILock); - MergeCrashAnnotations(aDst, crashReporterAPIData_Table); -} - // Adds crash time, uptime and memory report annotations static void AddCommonAnnotations(AnnotationTable& aAnnotations) { const time_t crashTime = time(nullptr); @@ -2461,23 +2626,8 @@ nsresult AppendAppNotesToCrashReport(const nsACString& data) { if (!GetEnabled()) return NS_ERROR_NOT_INITIALIZED; MutexAutoLock lock(*notesFieldLock); - notesField->Append(data); - return AnnotateCrashReport(Annotation::Notes, *notesField); -} - -// Returns true if found, false if not found. -static bool GetAnnotation(CrashReporter::Annotation key, nsACString& data) { - if (!gExceptionHandler) return false; - - MutexAutoLock lock(*crashReporterAPILock); - const nsCString& entry = crashReporterAPIData_Table[key]; - if (entry.IsEmpty()) { - return false; - } - - data = entry; - return true; + return NS_OK; } nsresult RegisterAppMemory(void* ptr, size_t length) { @@ -2513,15 +2663,19 @@ void SetIncludeContextHeap(bool aValue) { } bool GetServerURL(nsACString& aServerURL) { - if (!gExceptionHandler) return false; + if (!gExceptionHandler) { + return false; + } - return GetAnnotation(CrashReporter::Annotation::ServerURL, aServerURL); + aServerURL = gServerURL; + return true; } nsresult SetServerURL(const nsACString& aServerURL) { - // store server URL with the API data - // the client knows to handle this specially - return AnnotateCrashReport(Annotation::ServerURL, aServerURL); + // Store the server URL as an annotation, the crash reporter client knows how + // to handle this specially. + gServerURL = aServerURL; + return NS_OK; } nsresult SetRestartArgs(int argc, char** argv) { @@ -3107,12 +3261,89 @@ bool WriteExtraFile(const nsAString& id, const AnnotationTable& annotations) { return WriteExtraFile(pw, annotations); } +template <typename T> +static bool IsFixedSizeAnnotation(AnnotationContents& contents) { + return ((contents.tag == AnnotationContents::Tag::ByteBuffer) && + (contents.byte_buffer._0 == sizeof(T))) || + ((contents.tag == AnnotationContents::Tag::OwnedByteBuffer) && + (contents.owned_byte_buffer._0 == sizeof(T))); +} + // This adds annotations that were populated in the main process but are not // present among the ones that were passed in. Additionally common annotations // which are present in every crash report are added, including crash time, // uptime, etc... static void AddSharedAnnotations(AnnotationTable& aAnnotations) { - MergeContentCrashAnnotations(aAnnotations); + for (auto key : MakeEnumeratedRange(Annotation::Count)) { + AnnotationContents contents = {}; + nsAutoCString value; + size_t address = + mozannotation_get_contents(static_cast<uint32_t>(key), &contents); + + if (address) { + switch (TypeOfAnnotation(key)) { + case AnnotationType::String: + switch (contents.tag) { + case AnnotationContents::Tag::Empty: + break; + case AnnotationContents::Tag::CStringPointer: + address = *reinterpret_cast<size_t*>(address); + if (address == 0) { + break; + } + // FALLTHROUGH + case AnnotationContents::Tag::CString: + value.Assign(reinterpret_cast<const char*>(address)); + break; + case AnnotationContents::Tag::NSCStringPointer: + value.Assign(*reinterpret_cast<nsCString*>(address)); + break; + case AnnotationContents::Tag::ByteBuffer: + value.Assign(reinterpret_cast<const char*>(address), + contents.byte_buffer._0); + break; + case AnnotationContents::Tag::OwnedByteBuffer: + value.Assign(reinterpret_cast<const char*>(address), + contents.owned_byte_buffer._0); + break; + } + + break; + case AnnotationType::Boolean: + if (IsFixedSizeAnnotation<bool>(contents)) { + value.Assign(*reinterpret_cast<const bool*>(address) ? "1" : "0"); + } + break; + case AnnotationType::U32: + if (IsFixedSizeAnnotation<uint32_t>(contents)) { + value.AppendInt(*reinterpret_cast<const uint32_t*>(address)); + } + break; + case AnnotationType::U64: + if (IsFixedSizeAnnotation<uint64_t>(contents)) { + value.AppendInt(*reinterpret_cast<const uint64_t*>(address)); + } + break; + case AnnotationType::USize: + if (IsFixedSizeAnnotation<size_t>(contents)) { +#ifdef XP_MACOSX + // macOS defines size_t as unsigned long, which causes ambiguity + // when it comes to function overload, use a 64-bit integer instead + value.AppendInt(*reinterpret_cast<const uint64_t*>(address)); +#else + value.AppendInt(*reinterpret_cast<const size_t*>(address)); +#endif + } + break; + } + + if (!value.IsEmpty() && aAnnotations[key].IsEmpty() && + ShouldIncludeAnnotation(key, value.get())) { + aAnnotations[key] = value; + } + } + } + AddCommonAnnotations(aAnnotations); } @@ -3126,54 +3357,71 @@ static void AddChildProcessAnnotations( } for (const auto& annotation : *aChildAnnotations) { - switch (annotation.data.tag) { - case AnnotationData::Tag::Empty: - break; + Annotation id = static_cast<Annotation>(annotation.id); + const AnnotationData& data = annotation.data; - case AnnotationData::Tag::UsizeData: - if (annotation.id == - static_cast<uint32_t>(Annotation::OOMAllocationSize)) { - // We need to special-case OOMAllocationSize here because it should - // not be added if its value is 0. We'll come up with a more general - // method of skipping ignored values for crash annotations in the - // follow-ups. - if (annotation.data.usize_data._0 != 0) { - aAnnotations[static_cast<Annotation>(annotation.id)] = - nsPrintfCString("%zu", annotation.data.usize_data._0); - } - } else { - aAnnotations[static_cast<Annotation>(annotation.id)] = - nsPrintfCString("%zu", annotation.data.usize_data._0); + if ((id == Annotation::PHCBaseAddress) && + (data.tag == AnnotationData::Tag::ByteBuffer)) { + // PHC is special for now, let's deal with it here +#ifdef MOZ_PHC + const auto& buffer = data.byte_buffer._0; + mozilla::phc::AddrInfo addr_info; + memcpy(&addr_info, buffer.Elements(), sizeof(addr_info)); + PopulatePHCAnnotations(aAnnotations, &addr_info); +#endif + continue; + } + + if (data.tag == AnnotationData::Tag::Empty) { + continue; + } + + nsAutoCString value; + const uint8_t* buffer = data.byte_buffer._0.Elements(); + const size_t length = data.byte_buffer._0.Length(); + + switch (TypeOfAnnotation(id)) { + case AnnotationType::String: + value.Assign(reinterpret_cast<const char*>(buffer), length); + break; + case AnnotationType::Boolean: + if (length == sizeof(bool)) { + value.Assign(*reinterpret_cast<const bool*>(buffer) ? "1" : "0"); } break; - - case AnnotationData::Tag::NSCStringData: { - const auto& string = annotation.data.nsc_string_data._0; - if (!string.IsEmpty()) { - aAnnotations[static_cast<Annotation>(annotation.id)] = - annotation.data.nsc_string_data._0; + case AnnotationType::U32: + if (length == sizeof(uint32_t)) { + value.AppendInt(*reinterpret_cast<const uint32_t*>(buffer)); } - } break; - - case AnnotationData::Tag::ByteBuffer: { - if (annotation.id == - static_cast<uint32_t>(Annotation::PHCBaseAddress)) { -#ifdef MOZ_PHC - const auto& buffer = annotation.data.byte_buffer._0; - mozilla::phc::AddrInfo addr_info; - memcpy(&addr_info, buffer.Elements(), sizeof(addr_info)); - PopulatePHCAnnotations(aAnnotations, &addr_info); + break; + case AnnotationType::U64: + if (length == sizeof(uint64_t)) { + value.AppendInt(*reinterpret_cast<const uint64_t*>(buffer)); + } + break; + case AnnotationType::USize: + if (length == sizeof(size_t)) { +#ifdef XP_MACOSX + // macOS defines size_t as unsigned long, which causes ambiguity + // when it comes to function overload, use a 64-bit integer instead + value.AppendInt(*reinterpret_cast<const uint64_t*>(buffer)); +#else + value.AppendInt(*reinterpret_cast<const size_t*>(buffer)); #endif } - } break; + break; + } + + if (!value.IsEmpty() && ShouldIncludeAnnotation(id, value.get())) { + aAnnotations[id] = value; } } } // It really only makes sense to call this function when // ShouldReport() is true. -// Uses dumpFile's filename to generate memoryReport's filename (same name with -// a different extension) +// Uses dumpFile's filename to generate memoryReport's filename (same name +// with a different extension) static bool MoveToPending(nsIFile* dumpFile, nsIFile* extraFile, nsIFile* memoryReport) { nsCOMPtr<nsIFile> pendingDir; @@ -3480,44 +3728,15 @@ bool CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd) { bool SetRemoteExceptionHandler(const char* aCrashPipe) { MOZ_ASSERT(!gExceptionHandler, "crash client already init'd"); RegisterRuntimeExceptionModule(); - InitializeAnnotationFacilities(); - for (auto key : MakeEnumeratedRange(Annotation::Count)) { - switch (key) { - case Annotation::MozCrashReason: -#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY - case Annotation::MainThreadRunnableName: -#endif - case Annotation::OOMAllocationSize: -#ifdef MOZ_PHC - case Annotation::PHCBaseAddress: -#endif - break; - - default: - mozannotation_register_nscstring(static_cast<uint32_t>(key), - &crashReporterAPIData_Table[key]); - } - } - - mozannotation_register_cstring( - static_cast<uint32_t>(Annotation::MozCrashReason), &gMozCrashReason); - -#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY - mozannotation_register_char_buffer( - static_cast<uint32_t>(Annotation::MainThreadRunnableName), - &nsThread::sMainThreadRunnableName[0]); -#endif - - mozannotation_register_usize( - static_cast<uint32_t>(Annotation::OOMAllocationSize), - &gOOMAllocationSize); + InitializeAppNotes(); + RegisterAnnotations(); #ifdef MOZ_PHC // HACK: We're using the PHCBaseAddress annotation to point to the actual // PHC address information object. This is because we currently have no // difference between the internal representation of annotations and their - // external representation. Once we remove the old annotation API this will - // be properly addressed. + // external representation. Once we remove the old annotation API this + // will be properly addressed. mozannotation_register_bytebuffer( static_cast<uint32_t>(Annotation::PHCBaseAddress), &mozilla::phc::gAddrInfo, sizeof(mozilla::phc::gAddrInfo)); @@ -3876,7 +4095,8 @@ bool UnsetRemoteExceptionHandler(bool wasSet) { gExceptionHandler = nullptr; } #endif - TeardownAnnotationFacilities(); + gServerURL = ""; + TeardownAppNotes(); return true; } @@ -3894,9 +4114,9 @@ void SetNotificationPipeForChild(int childCrashFd) { // Bionic introduced support for getgrgid_r() and getgrnam_r() only in version // 24 (that is Android Nougat / 7.1.2). Since GeckoView is built by version 16 // (32-bit) or 21 (64-bit), those functions aren't defined, but nix needs them -// and minidump-writer relies on nix. These functions should never be called in -// practice hence we implement them only to satisfy nix linking requirements but -// we crash if we accidentally enter them. +// and minidump-writer relies on nix. These functions should never be called +// in practice hence we implement them only to satisfy nix linking +// requirements but we crash if we accidentally enter them. extern "C" { diff --git a/toolkit/crashreporter/nsExceptionHandler.h b/toolkit/crashreporter/nsExceptionHandler.h index f81cc07c8c..2564737137 100644 --- a/toolkit/crashreporter/nsExceptionHandler.h +++ b/toolkit/crashreporter/nsExceptionHandler.h @@ -18,12 +18,12 @@ #include "CrashAnnotations.h" -#include <stddef.h> -#include <stdint.h> #include "nsError.h" #include "nsString.h" #include "nsXULAppAPI.h" #include "prio.h" +#include <stddef.h> +#include <stdint.h> #if defined(XP_WIN) # ifdef WIN32_LEAN_AND_MEAN @@ -95,30 +95,42 @@ nsresult SetMinidumpPath(const nsAString& aPath); // child processes. Annotations added in the main process will be included in // child process crashes too unless the child process sets its own annotations. // If it does the child-provided annotation overrides the one set in the parent. -nsresult AnnotateCrashReport(Annotation key, bool data); -nsresult AnnotateCrashReport(Annotation key, int data); -nsresult AnnotateCrashReport(Annotation key, unsigned int data); -nsresult AnnotateCrashReport(Annotation key, const nsACString& data); -nsresult AppendToCrashReportAnnotation(Annotation key, const nsACString& data); -nsresult RemoveCrashReportAnnotation(Annotation key); +const bool* RegisterAnnotationBool(Annotation aKey, const bool* aData); +const uint32_t* RegisterAnnotationU32(Annotation aKey, const uint32_t* aData); +const uint64_t* RegisterAnnotationU64(Annotation aKey, const uint64_t* aData); +const size_t* RegisterAnnotationUSize(Annotation aKey, const size_t* aData); +const char* RegisterAnnotationCString(Annotation aKey, const char* aData); +const nsCString* RegisterAnnotationNSCString(Annotation aKey, + const nsCString* aData); + +nsresult RecordAnnotationBool(Annotation aKey, bool aData); +nsresult RecordAnnotationU32(Annotation aKey, uint32_t aData); +nsresult RecordAnnotationU64(Annotation aKey, uint64_t aData); +nsresult RecordAnnotationUSize(Annotation aKey, size_t aData); +nsresult RecordAnnotationCString(Annotation aKey, const char* aData); +nsresult RecordAnnotationNSCString(Annotation aKey, const nsACString& aData); +nsresult RecordAnnotationNSString(Annotation aKey, const nsAString& aData); +nsresult UnrecordAnnotation(Annotation aKey); + nsresult AppendAppNotesToCrashReport(const nsACString& data); // RAII class for setting a crash annotation during a limited scope of time. // Will reset the named annotation to its previous value when destroyed. // -// This type's behavior is identical to that of AnnotateCrashReport(). -class MOZ_RAII AutoAnnotateCrashReport final { +// This type's behavior is identical to that of RecordAnnotation(). +class MOZ_RAII AutoRecordAnnotation final { public: - AutoAnnotateCrashReport(Annotation key, bool data); - AutoAnnotateCrashReport(Annotation key, int data); - AutoAnnotateCrashReport(Annotation key, unsigned int data); - AutoAnnotateCrashReport(Annotation key, const nsACString& data); - ~AutoAnnotateCrashReport(); + AutoRecordAnnotation(Annotation key, bool data); + AutoRecordAnnotation(Annotation key, int data); + AutoRecordAnnotation(Annotation key, unsigned int data); + AutoRecordAnnotation(Annotation key, const nsACString& data); + ~AutoRecordAnnotation(); #ifdef MOZ_CRASHREPORTER private: Annotation mKey; - nsCString mPrevious; + const nsCString mCurrent; + const nsCString* mPrevious; #endif }; @@ -143,7 +155,8 @@ void GetAnnotation(uint32_t childPid, Annotation annotation, nsACString& outStr); // Functions for working with minidumps and .extras -typedef mozilla::EnumeratedArray<Annotation, Annotation::Count, nsCString> +typedef mozilla::EnumeratedArray<Annotation, nsCString, + size_t(Annotation::Count)> AnnotationTable; void DeleteMinidumpFilesForID( const nsAString& aId, diff --git a/toolkit/crashreporter/test/gtest/TestElfSoVersion.cpp b/toolkit/crashreporter/test/gtest/TestElfSoVersion.cpp new file mode 100644 index 0000000000..a6c91a8dff --- /dev/null +++ b/toolkit/crashreporter/test/gtest/TestElfSoVersion.cpp @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "gtest/gtest.h" +#include "mozilla/SpinEventLoopUntil.h" + +#include "linux_utils.h" + +#define ASSERT_EQ_UNSIGNED(v, e) ASSERT_EQ((v), (uint32_t)(e)) + +using namespace mozilla; + +class CrashReporter : public ::testing::Test {}; + +TEST_F(CrashReporter, ElfSoNoVersion) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libdbus1.so", version); + ASSERT_TRUE(rv); +} + +TEST_F(CrashReporter, ElfSo6) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libm.so.6", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 6); +} + +TEST_F(CrashReporter, ElfSoNormalShort) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libdbus1.so.1.2", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 1); + ASSERT_EQ_UNSIGNED(version[1], 2); +} + +TEST_F(CrashReporter, ElfSoNormalComplete) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libdbus1.so.1.2.3", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 1); + ASSERT_EQ_UNSIGNED(version[1], 2); + ASSERT_EQ_UNSIGNED(version[2], 3); +} + +TEST_F(CrashReporter, ElfSoNormalPrerelease) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libdbus1.so.1.2.3.98", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 1); + ASSERT_EQ_UNSIGNED(version[1], 2); + ASSERT_EQ_UNSIGNED(version[2], 3); + ASSERT_EQ_UNSIGNED(version[3], 98); +} + +TEST_F(CrashReporter, ElfSoNormalPrereleaseToomuch) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libdbus1.so.1.2.3.98.9.2.3", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 1); + ASSERT_EQ_UNSIGNED(version[1], 2); + ASSERT_EQ_UNSIGNED(version[2], 3); + ASSERT_EQ_UNSIGNED(version[3], 98); +} + +TEST_F(CrashReporter, ElfSoBig) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libatk-1.0.so.0.25009.1", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 0); + ASSERT_EQ_UNSIGNED(version[1], 25009); + ASSERT_EQ_UNSIGNED(version[2], 1); +} + +TEST_F(CrashReporter, ElfSoCairo) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libcairo.so.2.11800.3", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 2); + ASSERT_EQ_UNSIGNED(version[1], 11800); + ASSERT_EQ_UNSIGNED(version[2], 3); +} + +TEST_F(CrashReporter, ElfSoMax) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion( + "libcairo.so.2147483647.2147483647.2147483647.2147483647", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], INT32_MAX); + ASSERT_EQ_UNSIGNED(version[1], INT32_MAX); + ASSERT_EQ_UNSIGNED(version[2], INT32_MAX); + ASSERT_EQ_UNSIGNED(version[3], INT32_MAX); +} + +TEST_F(CrashReporter, ElfSoTimestamp) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libabsl_time_zone.so.20220623.0.0", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 20220623); + ASSERT_EQ_UNSIGNED(version[1], 0); + ASSERT_EQ_UNSIGNED(version[2], 0); +} + +TEST_F(CrashReporter, ElfSoChars) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libabsl_time_zone.so.1.2.3rc4", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 1); + ASSERT_EQ_UNSIGNED(version[1], 2); + ASSERT_EQ_UNSIGNED(version[2], 34); +} + +TEST_F(CrashReporter, ElfSoCharsMore) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libabsl_time_zone.so.1.2.3rc4.9", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 1); + ASSERT_EQ_UNSIGNED(version[1], 2); + ASSERT_EQ_UNSIGNED(version[2], 34); + ASSERT_EQ_UNSIGNED(version[3], 9); +} + +TEST_F(CrashReporter, ElfSoCharsOnly) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion("libabsl_time_zone.so.final", version); + ASSERT_TRUE(rv); +} + +TEST_F(CrashReporter, ElfSoNullVersion) { + bool rv = ElfFileSoVersion("libabsl_time_zone.so.1", nullptr); + ASSERT_FALSE(rv); +} + +TEST_F(CrashReporter, ElfSoFullPath) { + uint32_t version[4] = {0, 0, 0, 0}; + bool rv = ElfFileSoVersion( + "/usr/lib/x86_64-linux-gnu/libabsl_time_zone.so.20220623.0.0", version); + ASSERT_TRUE(rv); + ASSERT_EQ_UNSIGNED(version[0], 20220623); + ASSERT_EQ_UNSIGNED(version[1], 0); + ASSERT_EQ_UNSIGNED(version[2], 0); +} diff --git a/toolkit/crashreporter/test/gtest/moz.build b/toolkit/crashreporter/test/gtest/moz.build new file mode 100644 index 0000000000..9aa1c0a0db --- /dev/null +++ b/toolkit/crashreporter/test/gtest/moz.build @@ -0,0 +1,16 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Library("crashreportertest") + +if CONFIG["OS_ARCH"] == "Linux": + UNIFIED_SOURCES = [ + "TestElfSoVersion.cpp", + ] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" diff --git a/toolkit/crashreporter/test/moz.build b/toolkit/crashreporter/test/moz.build index 1ebd9b9029..254d232e0e 100644 --- a/toolkit/crashreporter/test/moz.build +++ b/toolkit/crashreporter/test/moz.build @@ -9,6 +9,10 @@ XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell.toml", "unit_ipc/xpcshell.toml"] if CONFIG["MOZ_PHC"]: XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell-phc.toml", "unit_ipc/xpcshell-phc.toml"] +TEST_DIRS += [ + "gtest", +] + BROWSER_CHROME_MANIFESTS += ["browser/browser.toml"] UNIFIED_SOURCES += [ diff --git a/toolkit/crashreporter/test/unit/head_crashreporter.js b/toolkit/crashreporter/test/unit/head_crashreporter.js index c37c8acf8c..34602e2107 100644 --- a/toolkit/crashreporter/test/unit/head_crashreporter.js +++ b/toolkit/crashreporter/test/unit/head_crashreporter.js @@ -156,6 +156,9 @@ async function handleMinidump(callback) { registerCleanupFunction(cleanup); Assert.ok(extrafile.exists()); + let data = await IOUtils.read(extrafile.path); + let decoder = new TextDecoder("ascii"); + console.log("data = " + decoder.decode(data)); let extra = await IOUtils.readJSON(extrafile.path); if (callback) { diff --git a/toolkit/crashreporter/test/unit/test_crash_modules_linux.js b/toolkit/crashreporter/test/unit/test_crash_modules_linux.js new file mode 100644 index 0000000000..9fcf587308 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_modules_linux.js @@ -0,0 +1,33 @@ +add_task(async function run_test() { + if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) { + dump( + "INFO | test_crash_modules.js | Can't test crashreporter in a non-libxul build.\n" + ); + return; + } + + await do_crash( + function () { + crashType = CrashTestUtils.CRASH_ABORT; + }, + async function (mdump, extra, extraFile) { + runMinidumpAnalyzer(mdump); + + // Refresh updated extra data + extra = await IOUtils.readJSON(extraFile.path); + + // Check modules' versions + const modules = extra.StackTraces.modules; + const version_regexp = /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/; + + for (let module of modules) { + console.debug("module", module); + console.debug("version => ", module.version); + console.debug("version regex => ", version_regexp.exec(module.version)); + Assert.notEqual(version_regexp.exec(module.version), null); + } + }, + // process will exit with a zero exit status + true + ); +}); diff --git a/toolkit/crashreporter/test/unit/xpcshell-phc.toml b/toolkit/crashreporter/test/unit/xpcshell-phc.toml index 278cf28193..1bb182d852 100644 --- a/toolkit/crashreporter/test/unit/xpcshell-phc.toml +++ b/toolkit/crashreporter/test/unit/xpcshell-phc.toml @@ -1,8 +1,8 @@ [DEFAULT] head = "head_crashreporter.js" skip-if = [ - "toolkit == 'android'", # 1536217 - "os == 'win' && msix", # https://bugzilla.mozilla.org/show_bug.cgi?id=1807922 + "os == 'android'", # 1536217 + "win11_2009 && msix", # https://bugzilla.mozilla.org/show_bug.cgi?id=1807922 ] support-files = [ "crasher_subprocess_head.js", diff --git a/toolkit/crashreporter/test/unit/xpcshell.toml b/toolkit/crashreporter/test/unit/xpcshell.toml index ffa631d0a1..6b1676ac32 100644 --- a/toolkit/crashreporter/test/unit/xpcshell.toml +++ b/toolkit/crashreporter/test/unit/xpcshell.toml @@ -40,6 +40,10 @@ run-if = ["os == 'win'"] reason = "Test covering Windows-specific module handling" run-sequentially = "very high failure rate in parallel" +["test_crash_modules_linux.js"] +run-if = ["os == 'linux'"] +reason = "Test covering Linux-specific module handling" + ["test_crash_moz_crash.js"] ["test_crash_oom.js"] @@ -51,7 +55,7 @@ run-sequentially = "very high failure rate in parallel" ["test_crash_rust_panic_multiline.js"] ["test_crash_stack_overflow.js"] -skip-if = ["os != 'linux'"] +run-if = ["os == 'linux'"] reason = "Still broken on macOS and not yet supported on Windows" ["test_crash_terminator.js"] @@ -60,71 +64,71 @@ reason = "Still broken on macOS and not yet supported on Windows" ["test_crash_win64cfi_alloc_large.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" ["test_crash_win64cfi_alloc_small.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" ["test_crash_win64cfi_epilog.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" ["test_crash_win64cfi_infinite_code_chain.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" support-files = ["test_crash_win64cfi_infinite_code_chain.exe"] ["test_crash_win64cfi_infinite_entry_chain.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" support-files = ["test_crash_win64cfi_infinite_entry_chain.exe"] ["test_crash_win64cfi_invalid_exception_rva.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" support-files = ["test_crash_win64cfi_invalid_exception_rva.exe"] ["test_crash_win64cfi_not_a_pe.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" support-files = ["test_crash_win64cfi_not_a_pe.exe"] ["test_crash_win64cfi_push_nonvol.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" ["test_crash_win64cfi_save_nonvol.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" ["test_crash_win64cfi_save_nonvol_far.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" ["test_crash_win64cfi_save_xmm128.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" ["test_crash_win64cfi_save_xmm128_far.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" ["test_crash_win64cfi_unknown_op.js"] head = "head_crashreporter.js head_win64cfi.js" -run-if = ["os == 'win' && bits == 64 && processor == 'x86_64'"] +run-if = ["os == 'win' && bits == 64"] reason = "Windows test specific to the x86-64 architecture" ["test_crash_with_memory_report.js"] @@ -134,8 +138,10 @@ reason = "Windows test specific to the x86-64 architecture" ["test_crashreporter_appmem.js"] # we need to skip this due to bug 838613 skip-if = [ - "os != 'win' && os != 'linux'", - "os=='linux' && bits==32", + "os == 'android'", + "apple_silicon", + "apple_catalina", + "os == 'linux' && os_version == '18.04' && bits == 32", ] ["test_crashreporter_crash.js"] @@ -149,4 +155,4 @@ run-sequentially = "very high failure rate in parallel" run-sequentially = "very high failure rate in parallel" ["test_override_exception_handler.js"] -skip-if = ["os != 'win'"] +run-if = ["os == 'win'"] diff --git a/toolkit/crashreporter/tools/symbolstore.py b/toolkit/crashreporter/tools/symbolstore.py index bc16002503..8bc7a7120a 100755 --- a/toolkit/crashreporter/tools/symbolstore.py +++ b/toolkit/crashreporter/tools/symbolstore.py @@ -578,7 +578,7 @@ class Dumper: # MODULE os cpu guid debug_file (guid, debug_file) = (module_line.split())[3:5] # strip off .pdb extensions, and append .sym - sym_file = re.sub("\.pdb$", "", debug_file) + ".sym" + sym_file = re.sub(r"\.pdb$", "", debug_file) + ".sym" # we do want forward slashes here rel_path = os.path.join(debug_file, guid, sym_file).replace("\\", "/") full_path = os.path.normpath(os.path.join(self.symbol_path, rel_path)) |