From 40cd1b72cfd8a6c56092ba5f4840e625fa66bc24 Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Wed, 11 Aug 2021 12:53:57 -0400 Subject: [PATCH] ios: Migrate ios/snapshot to writing intermediate dumps. This migrates all the logic that used to live in ios/snapshots that gathers all the various information during an exception. Everything in InProcessIntermediateDumpHandler is considered `RUNS-DURING-CRASH`. Change-Id: Icc47c9de0f66be2b14a46a13d1038176082a3218 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2920547 Commit-Queue: Justin Cohen Reviewed-by: Mark Mentovai --- client/BUILD.gn | 12 +- client/annotation.h | 9 +- client/annotation_list.h | 17 + client/crashpad_info.h | 13 + .../in_process_intermediate_dump_handler.cc | 1258 +++++++++++++++++ .../in_process_intermediate_dump_handler.h | 146 ++ ..._process_intermediate_dump_handler_test.cc | 242 ++++ ...xception_snapshot_ios_intermediate_dump.cc | 20 +- ...ess_snapshot_ios_intermediate_dump_test.cc | 6 +- snapshot/mac/process_reader_mac.cc | 2 +- .../crashreporterclient.proctype | 3 +- util/ios/ios_intermediate_dump_format.h | 10 +- util/ios/ios_system_data_collector.h | 10 +- util/ios/scoped_vm_read.cc | 2 +- util/ios/scoped_vm_read.h | 13 +- 15 files changed, 1723 insertions(+), 40 deletions(-) create mode 100644 client/ios_handler/in_process_intermediate_dump_handler.cc create mode 100644 client/ios_handler/in_process_intermediate_dump_handler.h create mode 100644 client/ios_handler/in_process_intermediate_dump_handler_test.cc diff --git a/client/BUILD.gn b/client/BUILD.gn index aa1eaaa4..5c18c6b0 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -35,6 +35,8 @@ crashpad_static_library("client") { "crashpad_client_ios.cc", "ios_handler/exception_processor.h", "ios_handler/exception_processor.mm", + "ios_handler/in_process_intermediate_dump_handler.cc", + "ios_handler/in_process_intermediate_dump_handler.h", "simulate_crash_ios.h", ] } @@ -165,13 +167,7 @@ source_set("client_test") { sources += [ "crashpad_client_ios_test.mm", "ios_handler/exception_processor_test.mm", - ] - sources -= [ - "annotation_list_test.cc", - "annotation_test.cc", - "crash_report_database_test.cc", - "prune_crash_reports_test.cc", - "settings_test.cc", + "ios_handler/in_process_intermediate_dump_handler_test.cc", ] } @@ -181,12 +177,12 @@ source_set("client_test") { deps = [ ":client", + "$mini_chromium_source_parent:base", "../compat", "../snapshot", "../test", "../third_party/googletest:googlemock", "../third_party/googletest:googletest", - "$mini_chromium_source_parent:base", "../util", ] diff --git a/client/annotation.h b/client/annotation.h index ddfe8f64..edb963c2 100644 --- a/client/annotation.h +++ b/client/annotation.h @@ -29,7 +29,11 @@ #include "build/build_config.h" namespace crashpad { - +#if defined(OS_IOS) +namespace internal { +class InProcessIntermediateDumpHandler; +} // namespace internal +#endif class AnnotationList; //! \brief Base class for an annotation, which records a name-value pair of @@ -167,6 +171,9 @@ class Annotation { protected: friend class AnnotationList; +#if defined(OS_IOS) + friend class internal::InProcessIntermediateDumpHandler; +#endif std::atomic& link_node() { return link_node_; } diff --git a/client/annotation_list.h b/client/annotation_list.h index 9485c46c..0b807688 100644 --- a/client/annotation_list.h +++ b/client/annotation_list.h @@ -16,9 +16,15 @@ #define CRASHPAD_CLIENT_ANNOTATION_LIST_H_ #include "base/macros.h" +#include "build/build_config.h" #include "client/annotation.h" namespace crashpad { +#if defined(OS_IOS) +namespace internal { +class InProcessIntermediateDumpHandler; +} // namespace internal +#endif //! \brief A list that contains all the currently set annotations. //! @@ -77,6 +83,17 @@ class AnnotationList { //! \brief Returns an iterator past the last element of the annotation list. Iterator end(); + protected: +#if defined(OS_IOS) + friend class internal::InProcessIntermediateDumpHandler; +#endif + + //! \brief Returns a pointer to the tail node. + const Annotation* tail_pointer() const { return tail_pointer_; } + + //! \brief Returns a pointer to the head element. + const Annotation* head() const { return &head_; } + private: // To make it easier for the handler to locate the dummy tail node, store the // pointer. Placed first for packing. diff --git a/client/crashpad_info.h b/client/crashpad_info.h index ed7b9c14..62efc10d 100644 --- a/client/crashpad_info.h +++ b/client/crashpad_info.h @@ -32,6 +32,10 @@ namespace crashpad { namespace internal { +#if defined(OS_IOS) +class InProcessIntermediateDumpHandler; +#endif + //! \brief A linked list of blocks representing custom streams in the minidump, //! with addresses (and size) stored as uint64_t to simplify reading from //! the handler process. @@ -223,6 +227,15 @@ struct CrashpadInfo { kSignature = 'CPad', }; + protected: +#if defined(OS_IOS) + friend class internal::InProcessIntermediateDumpHandler; +#endif + + uint32_t signature() const { return signature_; } + uint32_t version() const { return version_; } + uint32_t size() const { return size_; } + private: // The compiler won’t necessarily see anyone using these fields, but it // shouldn’t warn about that. These fields aren’t intended for use by the diff --git a/client/ios_handler/in_process_intermediate_dump_handler.cc b/client/ios_handler/in_process_intermediate_dump_handler.cc new file mode 100644 index 00000000..43552a7a --- /dev/null +++ b/client/ios_handler/in_process_intermediate_dump_handler.cc @@ -0,0 +1,1258 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "client/ios_handler/in_process_intermediate_dump_handler.h" + +#include +#include +#include +#include +#include +#include + +#include "base/cxx17_backports.h" +#include "build/build_config.h" +#include "snapshot/snapshot_constants.h" +#include "util/ios/ios_intermediate_dump_writer.h" +#include "util/ios/raw_logging.h" +#include "util/ios/scoped_vm_read.h" + +namespace crashpad { +namespace internal { + +namespace { + +#if defined(ARCH_CPU_X86_64) +const thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64; +const thread_state_flavor_t kFloatStateFlavor = x86_FLOAT_STATE64; +const thread_state_flavor_t kDebugStateFlavor = x86_DEBUG_STATE64; +using thread_state_type = x86_thread_state64_t; +#elif defined(ARCH_CPU_ARM64) +const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64; +const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64; +const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64; +using thread_state_type = arm_thread_state64_t; +#endif + +// From snapshot/mac/process_types/crashreporterclient.proctype +struct crashreporter_annotations_t { + uint64_t version; + uint64_t message; + uint64_t signature_string; + uint64_t backtrace; + uint64_t message2; + uint64_t thread; + uint64_t dialog_mode; + uint64_t abort_cause; +}; + +//! \brief Manage memory and ports after calling `task_threads`. +class ScopedTaskThreads { + public: + explicit ScopedTaskThreads(thread_act_array_t threads, + mach_msg_type_number_t thread_count) + : threads_(threads), thread_count_(thread_count) {} + + ~ScopedTaskThreads() { + for (uint32_t thread_index = 0; thread_index < thread_count_; + ++thread_index) { + mach_port_deallocate(mach_task_self(), threads_[thread_index]); + } + vm_deallocate(mach_task_self(), + reinterpret_cast(threads_), + sizeof(thread_t) * thread_count_); + } + + private: + thread_act_array_t threads_; + mach_msg_type_number_t thread_count_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTaskThreads); +}; + +//! \brief Log \a key as a string. +void WriteError(IntermediateDumpKey key) { + CRASHPAD_RAW_LOG("Unable to write key"); + switch (key) { +// clang-format off +#define CASE_KEY(Name, Value) \ + case IntermediateDumpKey::Name: \ + CRASHPAD_RAW_LOG(#Name); \ + break; + INTERMEDIATE_DUMP_KEYS(CASE_KEY) +#undef CASE_KEY + // clang-format on + } +} + +//! \brief Call AddProperty with raw error log. +//! +//! \param[in] writer The dump writer +//! \param[in] key The key to write. +//! \param[in] value Memory to be written. +//! \param[in] count Length of \a value. +template +void WriteProperty(IOSIntermediateDumpWriter* writer, + IntermediateDumpKey key, + const T* value, + size_t count = 1) { + if (!writer->AddProperty(key, value, count)) + WriteError(key); +} + +//! \brief Call AddPropertyBytes with raw error log. +//! +//! \param[in] writer The dump writer +//! \param[in] key The key to write. +//! \param[in] value Memory to be written. +//! \param[in] count Length of \a data. +void WritePropertyBytes(IOSIntermediateDumpWriter* writer, + IntermediateDumpKey key, + const void* value, + size_t value_length) { + if (!writer->AddPropertyBytes(key, value, value_length)) + WriteError(key); +} + +kern_return_t MachVMRegionRecurseDeepest(task_t task, + vm_address_t* address, + vm_size_t* size, + natural_t* depth, + vm_prot_t* protection, + unsigned int* user_tag) { + vm_region_submap_short_info_64 submap_info; + mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + while (true) { + // Note: vm_region_recurse() would be fine here, but it does not provide + // VM_REGION_SUBMAP_SHORT_INFO_COUNT. + kern_return_t kr = vm_region_recurse_64( + task, + address, + size, + depth, + reinterpret_cast(&submap_info), + &count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "vm_region_recurse_64"); + return kr; + } + + if (!submap_info.is_submap) { + *protection = submap_info.protection; + *user_tag = submap_info.user_tag; + return KERN_SUCCESS; + } + + ++*depth; + } +} + +//! \brief Adjusts the region for the red zone, if the ABI requires one. +//! +//! This method performs red zone calculation for CalculateStackRegion(). Its +//! parameters are local variables used within that method, and may be +//! modified as needed. +//! +//! Where a red zone is required, the region of memory captured for a thread’s +//! stack will be extended to include the red zone below the stack pointer, +//! provided that such memory is mapped, readable, and has the correct user +//! tag value. If these conditions cannot be met fully, as much of the red +//! zone will be captured as is possible while meeting these conditions. +//! +//! \param[in,out] start_address The base address of the region to begin +//! capturing stack memory from. On entry, \a start_address is the stack +//! pointer. On return, \a start_address may be decreased to encompass a +//! red zone. +//! \param[in,out] region_base The base address of the region that contains +//! stack memory. This is distinct from \a start_address in that \a +//! region_base will be page-aligned. On entry, \a region_base is the +//! base address of a region that contains \a start_address. On return, +//! if \a start_address is decremented and is outside of the region +//! originally described by \a region_base, \a region_base will also be +//! decremented appropriately. +//! \param[in,out] region_size The size of the region that contains stack +//! memory. This region begins at \a region_base. On return, if \a +//! region_base is decremented, \a region_size will be incremented +//! appropriately. +//! \param[in] user_tag The Mach VM system’s user tag for the region described +//! by the initial values of \a region_base and \a region_size. The red +//! zone will only be allowed to extend out of the region described by +//! these initial values if the user tag is appropriate for stack memory +//! and the expanded region has the same user tag value. +void LocateRedZone(vm_address_t* const start_address, + vm_address_t* const region_base, + vm_address_t* const region_size, + const unsigned int user_tag) { + // x86_64 has a red zone. See AMD64 ABI 0.99.8, + // https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/uploads/01de35b2c8adc7545de52604cc45d942/x86-64-psABI-2021-05-20.pdf#page=23. + // section 3.2.2, “The Stack Frame”. + // So does ARM64, + // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-Stacks-Red-Zone + // section "Respect the Stack’s Red Zone". + constexpr vm_size_t kRedZoneSize = 128; + vm_address_t red_zone_base = + *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0; + bool red_zone_ok = false; + if (red_zone_base >= *region_base) { + // The red zone is within the region already discovered. + red_zone_ok = true; + } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) { + // Probe to see if there’s a region immediately below the one already + // discovered. + vm_address_t red_zone_region_base = red_zone_base; + vm_size_t red_zone_region_size; + natural_t red_zone_depth = 0; + vm_prot_t red_zone_protection; + unsigned int red_zone_user_tag; + kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(), + &red_zone_region_base, + &red_zone_region_size, + &red_zone_depth, + &red_zone_protection, + &red_zone_user_tag); + if (kr != KERN_SUCCESS) { + *start_address = *region_base; + } else if (red_zone_region_base + red_zone_region_size == *region_base && + (red_zone_protection & VM_PROT_READ) != 0 && + red_zone_user_tag == user_tag) { + // The region containing the red zone is immediately below the region + // already found, it’s readable (not the guard region), and it has the + // same user tag as the region already found, so merge them. + red_zone_ok = true; + *region_base -= red_zone_region_size; + *region_size += red_zone_region_size; + } + } + + if (red_zone_ok) { + // Begin capturing from the base of the red zone (but not the entire + // region that encompasses the red zone). + *start_address = red_zone_base; + } else { + // The red zone would go lower into another region in memory, but no + // region was found. Memory can only be captured to an address as low as + // the base address of the region already found. + *start_address = *region_base; + } +} + +//! \brief Calculates the base address and size of the region used as a +//! thread’s stack. +//! +//! The region returned by this method may be formed by merging multiple +//! adjacent regions in a process’ memory map if appropriate. The base address +//! of the returned region may be lower than the \a stack_pointer passed in +//! when the ABI mandates a red zone below the stack pointer. +//! +//! \param[in] stack_pointer The stack pointer, referring to the top (lowest +//! address) of a thread’s stack. +//! \param[out] stack_region_size The size of the memory region used as the +//! thread’s stack. +//! +//! \return The base address (lowest address) of the memory region used as the +//! thread’s stack. +vm_address_t CalculateStackRegion(vm_address_t stack_pointer, + vm_size_t* stack_region_size) { + // For pthreads, it may be possible to compute the stack region based on the + // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct + // for a thread can be located at TSD slot 0, or the known offsets of + // stackaddr and stacksize from the TSD area could be used. + vm_address_t region_base = stack_pointer; + vm_size_t region_size; + natural_t depth = 0; + vm_prot_t protection; + unsigned int user_tag; + kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(), + ®ion_base, + ®ion_size, + &depth, + &protection, + &user_tag); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "MachVMRegionRecurseDeepest"); + *stack_region_size = 0; + return 0; + } + + if (region_base > stack_pointer) { + // There’s nothing mapped at the stack pointer’s address. Something may have + // trashed the stack pointer. Note that this shouldn’t happen for a normal + // stack guard region violation because the guard region is mapped but has + // VM_PROT_NONE protection. + *stack_region_size = 0; + return 0; + } + + vm_address_t start_address = stack_pointer; + + if ((protection & VM_PROT_READ) == 0) { + // If the region isn’t readable, the stack pointer probably points to the + // guard region. Don’t include it as part of the stack, and don’t include + // anything at any lower memory address. The code below may still possibly + // find the real stack region at a memory address higher than this region. + start_address = region_base + region_size; + } else { + // If the ABI requires a red zone, adjust the region to include it if + // possible. + LocateRedZone(&start_address, ®ion_base, ®ion_size, user_tag); + + // Regardless of whether the ABI requires a red zone, capture up to + // kExtraCaptureSize additional bytes of stack, but only if present in the + // region that was already found. + constexpr vm_size_t kExtraCaptureSize = 128; + start_address = std::max(start_address >= kExtraCaptureSize + ? start_address - kExtraCaptureSize + : start_address, + region_base); + + // Align start_address to a 16-byte boundary, which can help readers by + // ensuring that data is aligned properly. This could page-align instead, + // but that might be wasteful. + constexpr vm_size_t kDesiredAlignment = 16; + start_address &= ~(kDesiredAlignment - 1); + DCHECK_GE(start_address, region_base); + } + + region_size -= (start_address - region_base); + region_base = start_address; + + vm_size_t total_region_size = region_size; + + // The stack region may have gotten split up into multiple abutting regions. + // Try to coalesce them. This frequently happens for the main thread’s stack + // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region + // is split up due to an mprotect() or vm_protect() call. + // + // Stack regions created by the kernel and the pthreads library will be marked + // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions + // with the same tag should find an entire stack region. Checking that the + // protection on individual regions is not VM_PROT_NONE should guarantee that + // this algorithm doesn’t collect map entries belonging to another thread’s + // stack: well-behaved stacks (such as those created by the kernel and the + // pthreads library) have VM_PROT_NONE guard regions at their low-address + // ends. + // + // Other stack regions may not be so well-behaved and thus if user_tag is not + // VM_MEMORY_STACK, the single region that was found is used as-is without + // trying to merge it with other adjacent regions. + if (user_tag == VM_MEMORY_STACK) { + vm_address_t try_address = region_base; + vm_address_t original_try_address; + + while (try_address += region_size, + original_try_address = try_address, + (kr = MachVMRegionRecurseDeepest(mach_task_self(), + &try_address, + ®ion_size, + &depth, + &protection, + &user_tag) == KERN_SUCCESS) && + try_address == original_try_address && + (protection & VM_PROT_READ) != 0 && + user_tag == VM_MEMORY_STACK) { + total_region_size += region_size; + } + + if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) { + // Tolerate KERN_INVALID_ADDRESS because it will be returned when there + // are no more regions in the map at or above the specified |try_address|. + CRASHPAD_RAW_LOG_ERROR(kr, "MachVMRegionRecurseDeepest"); + } + } + + *stack_region_size = total_region_size; + return region_base; +} + +//! \brief Write data around \a address to intermediate dump. Must be called +//! from within a ScopedArray. +void MaybeCaptureMemoryAround(IOSIntermediateDumpWriter* writer, + uint64_t address) { + constexpr uint64_t non_address_offset = 0x10000; + if (address < non_address_offset) + return; + + constexpr uint64_t max_address = std::numeric_limits::max(); + + if (address > max_address - non_address_offset) + return; + + constexpr uint64_t kRegisterByteOffset = 128; + const uint64_t target = address - kRegisterByteOffset; + constexpr uint64_t size = 512; + static_assert(kRegisterByteOffset <= size / 2, "negative offset too large"); + + IOSIntermediateDumpWriter::ScopedArrayMap memory_region(writer); + WriteProperty( + writer, IntermediateDumpKey::kThreadContextMemoryRegionAddress, &address); + // Don't use WritePropertyBytes, this one will fail regularly if |target| + // cannot be read. + writer->AddPropertyBytes(IntermediateDumpKey::kThreadContextMemoryRegionData, + reinterpret_cast(target), + size); +} + +void CaptureMemoryPointedToByThreadState(IOSIntermediateDumpWriter* writer, + thread_state_type thread_state) { + IOSIntermediateDumpWriter::ScopedArray memory_regions( + writer, IntermediateDumpKey::kThreadContextMemoryRegions); + +#if defined(ARCH_CPU_X86_64) + MaybeCaptureMemoryAround(writer, thread_state.__rax); + MaybeCaptureMemoryAround(writer, thread_state.__rbx); + MaybeCaptureMemoryAround(writer, thread_state.__rcx); + MaybeCaptureMemoryAround(writer, thread_state.__rdx); + MaybeCaptureMemoryAround(writer, thread_state.__rdi); + MaybeCaptureMemoryAround(writer, thread_state.__rsi); + MaybeCaptureMemoryAround(writer, thread_state.__rbp); + MaybeCaptureMemoryAround(writer, thread_state.__r8); + MaybeCaptureMemoryAround(writer, thread_state.__r9); + MaybeCaptureMemoryAround(writer, thread_state.__r10); + MaybeCaptureMemoryAround(writer, thread_state.__r11); + MaybeCaptureMemoryAround(writer, thread_state.__r12); + MaybeCaptureMemoryAround(writer, thread_state.__r13); + MaybeCaptureMemoryAround(writer, thread_state.__r14); + MaybeCaptureMemoryAround(writer, thread_state.__r15); + MaybeCaptureMemoryAround(writer, thread_state.__rip); +#elif defined(ARCH_CPU_ARM_FAMILY) + MaybeCaptureMemoryAround(writer, thread_state.__pc); + for (size_t i = 0; i < base::size(thread_state.__x); ++i) { + MaybeCaptureMemoryAround(writer, thread_state.__x[i]); + } +#endif +} + +void WriteCrashpadSimpleAnnotationsDictionary(IOSIntermediateDumpWriter* writer, + CrashpadInfo* crashpad_info) { + if (!crashpad_info->simple_annotations()) + return; + + ScopedVMRead simple_annotations; + if (!simple_annotations.Read(crashpad_info->simple_annotations())) { + CRASHPAD_RAW_LOG("Unable to read simple annotations."); + return; + } + + const size_t count = simple_annotations->GetCount(); + if (!count) + return; + + IOSIntermediateDumpWriter::ScopedArray annotations_array( + writer, IntermediateDumpKey::kAnnotationsSimpleMap); + + SimpleStringDictionary::Entry* entries = + reinterpret_cast( + simple_annotations.get()); + for (size_t index = 0; index < count; index++) { + IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer); + const auto& entry = entries[index]; + size_t key_length = strnlen(entry.key, sizeof(entry.key)); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationName, + reinterpret_cast(entry.key), + key_length); + size_t value_length = strnlen(entry.value, sizeof(entry.value)); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationValue, + reinterpret_cast(entry.value), + value_length); + } +} + +void WriteAppleCrashReporterAnnotations( + IOSIntermediateDumpWriter* writer, + crashreporter_annotations_t* crash_info) { + // This number was totally made up out of nowhere, but it seems prudent to + // enforce some limit. + constexpr size_t kMaxMessageSize = 1024; + IOSIntermediateDumpWriter::ScopedMap annotation_map( + writer, IntermediateDumpKey::kAnnotationsCrashInfo); + if (crash_info->message) { + const size_t message_len = strnlen( + reinterpret_cast(crash_info->message), kMaxMessageSize); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationsCrashInfoMessage1, + reinterpret_cast(crash_info->message), + message_len); + } + if (crash_info->message2) { + const size_t message_len = strnlen( + reinterpret_cast(crash_info->message2), kMaxMessageSize); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationsCrashInfoMessage2, + reinterpret_cast(crash_info->message2), + message_len); + } +} + +void WriteDyldErrorStringAnnotation( + IOSIntermediateDumpWriter* writer, + const uint64_t address, + const symtab_command* symtab_command_ptr, + const dysymtab_command* dysymtab_command_ptr, + const segment_command_64* text_seg_ptr, + const segment_command_64* linkedit_seg_ptr, + vm_size_t slide) { + if (text_seg_ptr == nullptr || linkedit_seg_ptr == nullptr || + symtab_command_ptr == nullptr) { + return; + } + + ScopedVMRead symtab_command; + ScopedVMRead dysymtab_command; + ScopedVMRead text_seg; + ScopedVMRead linkedit_seg; + if (!symtab_command.Read(symtab_command_ptr) || + !text_seg.Read(text_seg_ptr) || !linkedit_seg.Read(linkedit_seg_ptr) || + (dysymtab_command_ptr && !dysymtab_command.Read(dysymtab_command_ptr))) { + CRASHPAD_RAW_LOG("Unable to load dyld symbol table."); + } + + uint64_t file_slide = + (linkedit_seg->vmaddr - text_seg->vmaddr) - linkedit_seg->fileoff; + uint64_t strings = address + (symtab_command->stroff + file_slide); + nlist_64* symbol_ptr = reinterpret_cast( + address + (symtab_command->symoff + file_slide)); + + // If a dysymtab is present, use it to filter the symtab for just the + // portion used for extdefsym. If no dysymtab is present, the entire symtab + // will need to be consulted. + uint32_t symbol_count = symtab_command->nsyms; + if (dysymtab_command_ptr) { + symbol_ptr += dysymtab_command->iextdefsym; + symbol_count = dysymtab_command->nextdefsym; + } + + for (uint32_t i = 0; i < symbol_count; i++, symbol_ptr++) { + ScopedVMRead symbol; + if (!symbol.Read(symbol_ptr)) { + CRASHPAD_RAW_LOG("Unable to load dyld symbol table symbol."); + return; + } + + if (!symbol->n_value) + continue; + + ScopedVMRead symbol_name; + if (!symbol_name.Read(strings + symbol->n_un.n_strx)) { + CRASHPAD_RAW_LOG("Unable to load dyld symbol name."); + } + + if (strcmp(symbol_name.get(), "_error_string") == 0) { + ScopedVMRead symbol_value; + if (!symbol_value.Read(symbol->n_value + slide)) { + CRASHPAD_RAW_LOG("Unable to load dyld symbol value."); + } + // 1024 here is distinct from kMaxMessageSize above, because it refers to + // a precisely-sized buffer inside dyld. + const size_t value_len = strnlen(symbol_value.get(), 1024); + if (value_len) { + WriteProperty(writer, + IntermediateDumpKey::kAnnotationsDyldErrorString, + symbol_value.get(), + value_len); + } + return; + } + + continue; + } +} + +} // namespace + +// static +void InProcessIntermediateDumpHandler::WriteHeader( + IOSIntermediateDumpWriter* writer) { + static constexpr uint8_t version = 1; + WriteProperty(writer, IntermediateDumpKey::kVersion, &version); +} + +// static +void InProcessIntermediateDumpHandler::WriteProcessInfo( + IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedMap process_map( + writer, IntermediateDumpKey::kProcessInfo); + + timeval snapshot_time; + if (gettimeofday(&snapshot_time, nullptr) == 0) { + WriteProperty(writer, IntermediateDumpKey::kSnapshotTime, &snapshot_time); + } else { + CRASHPAD_RAW_LOG("gettimeofday"); + } + + // Used by pid, parent pid and snapshot time. + kinfo_proc kern_proc_info; + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; + size_t len = sizeof(kern_proc_info); + if (sysctl(mib, base::size(mib), &kern_proc_info, &len, nullptr, 0) == 0) { + WriteProperty( + writer, IntermediateDumpKey::kPID, &kern_proc_info.kp_proc.p_pid); + WriteProperty(writer, + IntermediateDumpKey::kParentPID, + &kern_proc_info.kp_eproc.e_ppid); + WriteProperty(writer, + IntermediateDumpKey::kStartTime, + &kern_proc_info.kp_proc.p_starttime); + } else { + CRASHPAD_RAW_LOG("sysctl kern_proc_info"); + } + + // Used by user time and system time. + mach_task_basic_info task_basic_info; + mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT; + kern_return_t kr = task_info(mach_task_self(), + MACH_TASK_BASIC_INFO, + reinterpret_cast(&task_basic_info), + &task_basic_info_count); + if (kr == KERN_SUCCESS) { + IOSIntermediateDumpWriter::ScopedMap task_info( + writer, IntermediateDumpKey::kTaskBasicInfo); + + WriteProperty( + writer, IntermediateDumpKey::kUserTime, &task_basic_info.user_time); + WriteProperty( + writer, IntermediateDumpKey::kSystemTime, &task_basic_info.system_time); + } else { + CRASHPAD_RAW_LOG("task_info task_basic_info"); + } + + task_thread_times_info_data_t task_thread_times; + mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT; + kr = task_info(mach_task_self(), + TASK_THREAD_TIMES_INFO, + reinterpret_cast(&task_thread_times), + &task_thread_times_count); + if (kr == KERN_SUCCESS) { + IOSIntermediateDumpWriter::ScopedMap task_thread_times_map( + writer, IntermediateDumpKey::kTaskThreadTimes); + + WriteProperty( + writer, IntermediateDumpKey::kUserTime, &task_thread_times.user_time); + WriteProperty(writer, + IntermediateDumpKey::kSystemTime, + &task_thread_times.system_time); + } else { + CRASHPAD_RAW_LOG("task_info task_basic_info"); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteSystemInfo( + IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data) { + IOSIntermediateDumpWriter::ScopedMap system_map( + writer, IntermediateDumpKey::kSystemInfo); + + const std::string& machine_description = system_data.MachineDescription(); + WriteProperty(writer, + IntermediateDumpKey::kMachineDescription, + machine_description.c_str(), + machine_description.length()); + int os_version_major; + int os_version_minor; + int os_version_bugfix; + system_data.OSVersion( + &os_version_major, &os_version_minor, &os_version_bugfix); + WriteProperty( + writer, IntermediateDumpKey::kOSVersionMajor, &os_version_major); + WriteProperty( + writer, IntermediateDumpKey::kOSVersionMinor, &os_version_minor); + WriteProperty( + writer, IntermediateDumpKey::kOSVersionBugfix, &os_version_bugfix); + const std::string& os_version_build = system_data.Build(); + WriteProperty(writer, + IntermediateDumpKey::kOSVersionBuild, + os_version_build.c_str(), + os_version_build.length()); + + int cpu_count = system_data.ProcessorCount(); + WriteProperty(writer, IntermediateDumpKey::kCpuCount, &cpu_count); + const std::string& cpu_vendor = system_data.CPUVendor(); + WriteProperty(writer, + IntermediateDumpKey::kCpuVendor, + cpu_vendor.c_str(), + cpu_vendor.length()); + + bool has_daylight_saving_time = system_data.HasDaylightSavingTime(); + WriteProperty(writer, + IntermediateDumpKey::kHasDaylightSavingTime, + &has_daylight_saving_time); + bool is_daylight_saving_time = system_data.IsDaylightSavingTime(); + WriteProperty(writer, + IntermediateDumpKey::kIsDaylightSavingTime, + &is_daylight_saving_time); + int standard_offset_seconds = system_data.StandardOffsetSeconds(); + WriteProperty(writer, + IntermediateDumpKey::kStandardOffsetSeconds, + &standard_offset_seconds); + int daylight_offset_seconds = system_data.DaylightOffsetSeconds(); + WriteProperty(writer, + IntermediateDumpKey::kDaylightOffsetSeconds, + &daylight_offset_seconds); + const std::string& standard_name = system_data.StandardName(); + WriteProperty(writer, + IntermediateDumpKey::kStandardName, + standard_name.c_str(), + standard_name.length()); + const std::string& daylight_name = system_data.DaylightName(); + WriteProperty(writer, + IntermediateDumpKey::kDaylightName, + daylight_name.c_str(), + daylight_name.length()); + + vm_size_t page_size; + host_page_size(mach_host_self(), &page_size); + WriteProperty(writer, IntermediateDumpKey::kPageSize, &page_size); + + mach_msg_type_number_t host_size = + sizeof(vm_statistics_data_t) / sizeof(integer_t); + vm_statistics_data_t vm_stat; + kern_return_t kr = host_statistics(mach_host_self(), + HOST_VM_INFO, + reinterpret_cast(&vm_stat), + &host_size); + if (kr == KERN_SUCCESS) { + IOSIntermediateDumpWriter::ScopedMap vm_stat_map( + writer, IntermediateDumpKey::kVMStat); + + WriteProperty(writer, IntermediateDumpKey::kActive, &vm_stat.active_count); + WriteProperty( + writer, IntermediateDumpKey::kInactive, &vm_stat.inactive_count); + WriteProperty(writer, IntermediateDumpKey::kWired, &vm_stat.wire_count); + WriteProperty(writer, IntermediateDumpKey::kFree, &vm_stat.free_count); + } else { + CRASHPAD_RAW_LOG("host_statistics"); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteThreadInfo( + IOSIntermediateDumpWriter* writer, + const uint64_t* frames, + const size_t num_frames) { + IOSIntermediateDumpWriter::ScopedArray thread_array( + writer, IntermediateDumpKey::kThreads); + + // Exception thread ID. + uint64_t exception_thread_id = 0; + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = + thread_info(mach_thread_self(), + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { + exception_thread_id = identifier_info.thread_id; + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_IDENTIFIER_INFO"); + } + + mach_msg_type_number_t thread_count = 0; + thread_act_array_t threads; + kr = task_threads(mach_task_self(), &threads, &thread_count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "task_threads"); + } + ScopedTaskThreads threads_vm_owner(threads, thread_count); + + for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) { + IOSIntermediateDumpWriter::ScopedArrayMap thread_map(writer); + thread_t thread = threads[thread_index]; + + thread_basic_info basic_info; + mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; + kr = thread_info(thread, + THREAD_BASIC_INFO, + reinterpret_cast(&basic_info), + &count); + if (kr == KERN_SUCCESS) { + WriteProperty(writer, + IntermediateDumpKey::kSuspendCount, + &basic_info.suspend_count); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_BASIC_INFO"); + } + + thread_precedence_policy precedence; + count = THREAD_PRECEDENCE_POLICY_COUNT; + boolean_t get_default = FALSE; + kr = thread_policy_get(thread, + THREAD_PRECEDENCE_POLICY, + reinterpret_cast(&precedence), + &count, + &get_default); + if (kr == KERN_SUCCESS) { + WriteProperty( + writer, IntermediateDumpKey::kPriority, &precedence.importance); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_policy_get"); + } + + // Thread ID. + uint64_t thread_id; + thread_identifier_info identifier_info; + count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info(thread, + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { + thread_id = identifier_info.thread_id; + WriteProperty( + writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id); + WriteProperty(writer, + IntermediateDumpKey::kThreadDataAddress, + &identifier_info.thread_handle); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_IDENTIFIER_INFO"); + } + + // thread_snapshot_ios_intermediate_dump::GenerateStackMemoryFromFrames is + // only implemented for arm64, so no x86_64 block here. +#if defined(ARCH_CPU_ARM64) + // For uncaught NSExceptions, use the frames passed from the system rather + // than the current thread state. + if (num_frames > 0 && exception_thread_id == thread_id) { + WriteProperty(writer, + IntermediateDumpKey::kThreadUncaughtNSExceptionFrames, + frames, + num_frames); + continue; + } +#endif + +#if defined(ARCH_CPU_X86_64) + x86_thread_state64_t thread_state; + x86_float_state64_t float_state; + x86_debug_state64_t debug_state; + mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT; + mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT; + mach_msg_type_number_t debug_state_count = x86_DEBUG_STATE64_COUNT; +#elif defined(ARCH_CPU_ARM64) + arm_thread_state64_t thread_state; + arm_neon_state64_t float_state; + arm_debug_state64_t debug_state; + mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; + mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT; + mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT; +#endif + + kern_return_t kr = + thread_get_state(thread, + kThreadStateFlavor, + reinterpret_cast(&thread_state), + &thread_state_count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_get_state::kThreadStateFlavor"); + } + WriteProperty(writer, IntermediateDumpKey::kThreadState, &thread_state); + + kr = thread_get_state(thread, + kFloatStateFlavor, + reinterpret_cast(&float_state), + &float_state_count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_get_state::kFloatStateFlavor"); + } + WriteProperty(writer, IntermediateDumpKey::kFloatState, &float_state); + + kr = thread_get_state(thread, + kDebugStateFlavor, + reinterpret_cast(&debug_state), + &debug_state_count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_get_state::kDebugStateFlavor"); + } + WriteProperty(writer, IntermediateDumpKey::kDebugState, &debug_state); + +#if defined(ARCH_CPU_X86_64) + vm_address_t stack_pointer = thread_state.__rsp; +#elif defined(ARCH_CPU_ARM64) + vm_address_t stack_pointer = thread_state.__sp; +#endif + + vm_size_t stack_region_size; + const vm_address_t stack_region_address = + CalculateStackRegion(stack_pointer, &stack_region_size); + WriteProperty(writer, + IntermediateDumpKey::kStackRegionAddress, + &stack_region_address); + WritePropertyBytes(writer, + IntermediateDumpKey::kStackRegionData, + reinterpret_cast(stack_region_address), + stack_region_size); + + // Grab extra memory from context. + CaptureMemoryPointedToByThreadState(writer, thread_state); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteModuleInfo( + IOSIntermediateDumpWriter* writer) { +#ifndef ARCH_CPU_64_BITS +#error Only 64-bit Mach-O is supported +#endif + + IOSIntermediateDumpWriter::ScopedArray module_array( + writer, IntermediateDumpKey::kModules); + + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t kr = task_info(mach_task_self(), + TASK_DYLD_INFO, + reinterpret_cast(&dyld_info), + &count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "task_info"); + } + + ScopedVMRead image_infos; + if (!image_infos.Read(dyld_info.all_image_info_addr)) { + CRASHPAD_RAW_LOG("Unable to dyld_info.all_image_info_addr"); + return; + } + + uint32_t image_count = image_infos->infoArrayCount; + const dyld_image_info* image_array = image_infos->infoArray; + for (uint32_t image_index = 0; image_index < image_count; ++image_index) { + IOSIntermediateDumpWriter::ScopedArrayMap modules(writer); + ScopedVMRead image; + if (!image.Read(&image_array[image_index])) { + CRASHPAD_RAW_LOG("Unable to dyld_image_info"); + return; + } + + WriteProperty(writer, + IntermediateDumpKey::kName, + image->imageFilePath, + strlen(image->imageFilePath)); + uint64_t address = FromPointerCast(image->imageLoadAddress); + WriteProperty(writer, IntermediateDumpKey::kAddress, &address); + WriteProperty( + writer, IntermediateDumpKey::kTimestamp, &image->imageFileModDate); + WriteModuleInfoAtAddress(writer, address, false /*is_dyld=false*/); + } + + { + IOSIntermediateDumpWriter::ScopedArrayMap modules(writer); + WriteProperty(writer, IntermediateDumpKey::kName, image_infos->dyldPath); + uint64_t address = + FromPointerCast(image_infos->dyldImageLoadAddress); + WriteProperty(writer, IntermediateDumpKey::kAddress, &address); + WriteModuleInfoAtAddress(writer, address, true /*is_dyld=true*/); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteExceptionFromSignal( + IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data, + siginfo_t* siginfo, + ucontext_t* context) { + IOSIntermediateDumpWriter::ScopedMap signal_exception_map( + writer, IntermediateDumpKey::kSignalException); + + WriteProperty(writer, IntermediateDumpKey::kSignalNumber, &siginfo->si_signo); + WriteProperty(writer, IntermediateDumpKey::kSignalCode, &siginfo->si_code); + WriteProperty(writer, IntermediateDumpKey::kSignalAddress, &siginfo->si_addr); +#if defined(ARCH_CPU_X86_64) + WriteProperty( + writer, IntermediateDumpKey::kThreadState, &context->uc_mcontext->__ss); + WriteProperty( + writer, IntermediateDumpKey::kFloatState, &context->uc_mcontext->__fs); +#elif defined(ARCH_CPU_ARM64) + WriteProperty( + writer, IntermediateDumpKey::kThreadState, &context->uc_mcontext->__ss); + WriteProperty( + writer, IntermediateDumpKey::kFloatState, &context->uc_mcontext->__ns); +#else +#error Port to your CPU architecture +#endif + + // Thread ID. + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = + thread_info(mach_thread_self(), + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { + WriteProperty( + writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::self"); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteExceptionFromMachException( + IOSIntermediateDumpWriter* writer, + exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + IOSIntermediateDumpWriter::ScopedMap mach_exception_map( + writer, IntermediateDumpKey::kMachException); + + WriteProperty(writer, IntermediateDumpKey::kException, &exception); + WriteProperty(writer, IntermediateDumpKey::kCodes, code, code_count); + WriteProperty(writer, IntermediateDumpKey::kFlavor, &flavor); + WritePropertyBytes(writer, + IntermediateDumpKey::kState, + state, + state_count * sizeof(uint32_t)); + + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = + thread_info(exception_thread, + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { + WriteProperty( + writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info"); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteExceptionFromNSException( + IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedMap nsexception_map( + writer, IntermediateDumpKey::kNSException); + + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = + thread_info(mach_thread_self(), + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { + WriteProperty( + writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::self"); + } +} + +void InProcessIntermediateDumpHandler::WriteModuleInfoAtAddress( + IOSIntermediateDumpWriter* writer, + uint64_t address, + bool is_dyld) { + ScopedVMRead header; + if (!header.Read(address) || header->magic != MH_MAGIC_64) { + CRASHPAD_RAW_LOG("Invalid module header"); + return; + } + + const load_command* command_ptr = reinterpret_cast( + reinterpret_cast(address) + 1); + + ScopedVMRead command; + if (!command.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid module command"); + return; + } + + // Make sure that the basic load command structure doesn’t overflow the + // space allotted for load commands, as well as iterating through ncmds. + vm_size_t slide = 0; + const symtab_command* symtab_command = nullptr; + const dysymtab_command* dysymtab_command = nullptr; + const segment_command_64* linkedit_seg = nullptr; + const segment_command_64* text_seg = nullptr; + for (uint32_t cmd_index = 0, cumulative_cmd_size = 0; + cmd_index <= header->ncmds && cumulative_cmd_size < header->sizeofcmds; + ++cmd_index, cumulative_cmd_size += command->cmdsize) { + if (command->cmd == LC_SEGMENT_64) { + ScopedVMRead segment; + if (!segment.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid LC_SEGMENT_64 segment"); + return; + } + const segment_command_64* segment_ptr = + reinterpret_cast(command_ptr); + if (strcmp(segment->segname, SEG_TEXT) == 0) { + text_seg = segment_ptr; + WriteProperty(writer, IntermediateDumpKey::kSize, &segment->vmsize); + slide = address - segment->vmaddr; + } else if (strcmp(segment->segname, SEG_DATA) == 0) { + WriteDataSegmentAnnotations(writer, segment_ptr, slide); + } else if (strcmp(segment->segname, SEG_LINKEDIT) == 0) { + linkedit_seg = segment_ptr; + } + } else if (command->cmd == LC_SYMTAB) { + symtab_command = + reinterpret_cast(command_ptr); + } else if (command->cmd == LC_DYSYMTAB) { + dysymtab_command = + reinterpret_cast(command_ptr); + } else if (command->cmd == LC_ID_DYLIB) { + ScopedVMRead dylib; + if (!dylib.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid LC_ID_DYLIB segment"); + return; + } + WriteProperty(writer, + IntermediateDumpKey::kDylibCurrentVersion, + &dylib->dylib.current_version); + } else if (command->cmd == LC_SOURCE_VERSION) { + ScopedVMRead source_version; + if (!source_version.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid LC_SOURCE_VERSION segment"); + return; + } + WriteProperty(writer, + IntermediateDumpKey::kSourceVersion, + &source_version->version); + } else if (command->cmd == LC_UUID) { + ScopedVMRead uuid; + if (!uuid.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid LC_UUID segment"); + return; + } + WriteProperty(writer, IntermediateDumpKey::kUUID, &uuid->uuid); + } + + command_ptr = reinterpret_cast( + reinterpret_cast(command_ptr) + command->cmdsize); + if (!command.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid module command"); + return; + } + } + + WriteProperty(writer, IntermediateDumpKey::kFileType, &header->filetype); + + if (is_dyld && header->filetype == MH_DYLINKER) { + WriteDyldErrorStringAnnotation(writer, + address, + symtab_command, + dysymtab_command, + text_seg, + linkedit_seg, + slide); + } +} + +void InProcessIntermediateDumpHandler::WriteDataSegmentAnnotations( + IOSIntermediateDumpWriter* writer, + const segment_command_64* segment_ptr, + vm_size_t slide) { + ScopedVMRead segment; + if (!segment.Read(segment_ptr)) { + CRASHPAD_RAW_LOG("Unable to read SEG_DATA."); + return; + } + const section_64* section_ptr = reinterpret_cast( + reinterpret_cast(segment_ptr) + sizeof(segment_command_64)); + for (uint32_t sect_index = 0; sect_index <= segment->nsects; ++sect_index) { + ScopedVMRead section; + if (!section.Read(section_ptr)) { + CRASHPAD_RAW_LOG("Unable to read SEG_DATA section."); + return; + } + if (strcmp(section->sectname, "crashpad_info") == 0) { + ScopedVMRead crashpad_info; + if (crashpad_info.Read(section->addr + slide) && + crashpad_info->size() == sizeof(CrashpadInfo) && + crashpad_info->signature() == CrashpadInfo::kSignature && + crashpad_info->version() == 1) { + WriteCrashpadAnnotationsList(writer, crashpad_info.get()); + WriteCrashpadSimpleAnnotationsDictionary(writer, crashpad_info.get()); + } + } else if (strcmp(section->sectname, "__crash_info") == 0) { + ScopedVMRead crash_info; + if (!crash_info.Read(section->addr + slide) || + (crash_info->version != 4 && crash_info->version != 5)) { + continue; + } + WriteAppleCrashReporterAnnotations(writer, crash_info.get()); + } + section_ptr = reinterpret_cast( + reinterpret_cast(section_ptr) + sizeof(section_64)); + } +} + +void InProcessIntermediateDumpHandler::WriteCrashpadAnnotationsList( + IOSIntermediateDumpWriter* writer, + CrashpadInfo* crashpad_info) { + if (!crashpad_info->annotations_list()) { + return; + } + ScopedVMRead annotation_list; + if (!annotation_list.Read(crashpad_info->annotations_list())) { + CRASHPAD_RAW_LOG("Unable to read annotations list object"); + return; + } + + IOSIntermediateDumpWriter::ScopedArray annotations_array( + writer, IntermediateDumpKey::kAnnotationObjects); + ScopedVMRead current; + if (!current.Read(annotation_list->head())) { + CRASHPAD_RAW_LOG("Unable to read annotation"); + return; + } + + for (size_t index = 0; + current->link_node() != annotation_list.get()->tail_pointer() && + index < kMaxNumberOfAnnotations; + ++index) { + ScopedVMRead node; + if (!node.Read(current->link_node())) { + CRASHPAD_RAW_LOG("Unable to read annotation"); + return; + } + current.Read(current->link_node()); + + if (node->size() == 0) + continue; + + if (node->size() > Annotation::kValueMaxSize) { + CRASHPAD_RAW_LOG("Incorrect annotation length"); + continue; + } + + IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer); + const size_t name_len = strnlen(reinterpret_cast(node->name()), + Annotation::kNameMaxLength); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationName, + reinterpret_cast(node->name()), + name_len); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationValue, + reinterpret_cast(node->value()), + node->size()); + Annotation::Type type = node->type(); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationType, + reinterpret_cast(&type), + sizeof(type)); + } +} + +} // namespace internal +} // namespace crashpad diff --git a/client/ios_handler/in_process_intermediate_dump_handler.h b/client/ios_handler/in_process_intermediate_dump_handler.h new file mode 100644 index 00000000..9a33ff49 --- /dev/null +++ b/client/ios_handler/in_process_intermediate_dump_handler.h @@ -0,0 +1,146 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_ +#define CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_ + +#include +#include +#include +#include + +#include "client/crashpad_info.h" +#include "util/ios/ios_intermediate_dump_writer.h" +#include "util/ios/ios_system_data_collector.h" +#include "util/mach/mach_extensions.h" + +namespace crashpad { +namespace internal { + +//! \brief Dump all in-process data to iOS intermediate dump. +//! Note: All methods are `RUNS-DURING-CRASH`. +class InProcessIntermediateDumpHandler final { + public: + //! \brief Set kVersion to 1. + //! + //! \param[in] writer The dump writer + static void WriteHeader(IOSIntermediateDumpWriter* writer); + + //! \brief Write ProcessSnapshot data to the intermediate dump. + //! + //! \param[in] writer The dump writer + static void WriteProcessInfo(IOSIntermediateDumpWriter* writer); + + //! \brief Write SystemSnapshot data to the intermediate dump. + //! + //! \param[in] writer The dump writer + static void WriteSystemInfo(IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data); + + //! \brief Write ThreadSnapshot data to the intermediate dump. + //! + //! For uncaught NSExceptions, \a frames and \a num_frames will be added to + //! the intermediate dump for the exception thread. Otherwise, or for the + //! remaining threads, use `thread_get_state`. + //! + //! \param[in] writer The dump writer + //! \param[in] frames An array of callstack return addresses. + //! \param[in] num_frames The number of callstack return address in \a frames. + static void WriteThreadInfo(IOSIntermediateDumpWriter* writer, + const uint64_t* frames, + const size_t num_frames); + + //! \brief Write ModuleSnapshot data to the intermediate dump. + //! + //! This includes both modules and annotations. + //! + //! \param[in] writer The dump writer + static void WriteModuleInfo(IOSIntermediateDumpWriter* writer); + + //! \brief Write an ExceptionSnapshot from a signal to the intermediate dump. + //! + //! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException + //! and WriteExceptionFromNSException should be called per intermediate dump. + //! + //! \param[in] writer The dump writer + //! \param[in] system_data An object containing various system data points. + //! \param[in] siginfo A pointer to a `siginfo_t` object received by a signal + //! handler. + //! \param[in] context A pointer to a `ucontext_t` object received by a + //! signal. + static void WriteExceptionFromSignal( + IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data, + siginfo_t* siginfo, + ucontext_t* context); + + //! \brief Write an ExceptionSnapshot from a mach exception to the + //! intermediate dump. + //! + //! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException + //! and WriteExceptionFromNSException should be called per intermediate dump. + //! + //! \param[in] writer The dump writer + //! \param[in] system_data An object containing various system data points. + //! \param[in] behavior + //! \param[in] thread + //! \param[in] exception + //! \param[in] code + //! \param[in] code_count + //! \param[in] flavor + //! \param[in] old_state + //! \param[in] old_state_count + static void WriteExceptionFromMachException( + IOSIntermediateDumpWriter* writer, + exception_behavior_t behavior, + thread_t thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count); + + //! \brief Write an ExceptionSnapshot from an NSException to the + //! intermediate dump. + //! + //! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException + //! and WriteExceptionFromNSException should be called per intermediate dump. + //! + //! \param[in] writer The dump writer + static void WriteExceptionFromNSException(IOSIntermediateDumpWriter* writer); + + private: + //! \brief Parse and extract module and annotation information from header. + static void WriteModuleInfoAtAddress(IOSIntermediateDumpWriter* writer, + uint64_t address, + bool is_dyld); + + //! \brief Extract and write Apple crashreporter_annotations_t data and + //! Crashpad annotations. + static void WriteDataSegmentAnnotations(IOSIntermediateDumpWriter* writer, + const segment_command_64* segment_ptr, + vm_size_t slide); + + //! \brief Write Crashpad annotations list. + static void WriteCrashpadAnnotationsList(IOSIntermediateDumpWriter* writer, + CrashpadInfo* crashpad_info); + + DISALLOW_IMPLICIT_CONSTRUCTORS(InProcessIntermediateDumpHandler); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_ diff --git a/client/ios_handler/in_process_intermediate_dump_handler_test.cc b/client/ios_handler/in_process_intermediate_dump_handler_test.cc new file mode 100644 index 00000000..9616ce75 --- /dev/null +++ b/client/ios_handler/in_process_intermediate_dump_handler_test.cc @@ -0,0 +1,242 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "client/ios_handler/in_process_intermediate_dump_handler.h" + +#include + +#include "base/cxx17_backports.h" +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h" +#include "test/scoped_temp_dir.h" +#include "test/test_paths.h" +#include "util/file/filesystem.h" +#include "util/misc/capture_context.h" + +namespace crashpad { +namespace test { +namespace { + +using internal::InProcessIntermediateDumpHandler; + +class InProcessIntermediateDumpHandlerTest : public testing::Test { + protected: + // testing::Test: + + void SetUp() override { + path_ = temp_dir_.path().Append("dump_file"); + writer_ = std::make_unique(); + EXPECT_TRUE(writer_->Open(path_)); + ASSERT_TRUE(IsRegularFile(path_)); + } + + void TearDown() override { + writer_.reset(); + EXPECT_FALSE(IsRegularFile(path_)); + } + + void WriteReport() { + internal::IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer_.get()); + InProcessIntermediateDumpHandler::WriteHeader(writer_.get()); + InProcessIntermediateDumpHandler::WriteProcessInfo(writer_.get()); + InProcessIntermediateDumpHandler::WriteSystemInfo(writer_.get(), + system_data_); + InProcessIntermediateDumpHandler::WriteThreadInfo(writer_.get(), 0, 0); + InProcessIntermediateDumpHandler::WriteModuleInfo(writer_.get()); + } + + void WriteMachException() { + crashpad::NativeCPUContext cpu_context; + crashpad::CaptureContext(&cpu_context); + const mach_exception_data_type_t code[2] = {}; + static constexpr int kSimulatedException = -1; + InProcessIntermediateDumpHandler::WriteExceptionFromMachException( + writer_.get(), + MACH_EXCEPTION_CODES, + mach_thread_self(), + kSimulatedException, + code, + base::size(code), + MACHINE_THREAD_STATE, + reinterpret_cast(&cpu_context), + MACHINE_THREAD_STATE_COUNT); + } + + const auto& path() const { return path_; } + auto writer() const { return writer_.get(); } + + private: + std::unique_ptr writer_; + internal::IOSSystemDataCollector system_data_; + ScopedTempDir temp_dir_; + base::FilePath path_; +}; + +TEST_F(InProcessIntermediateDumpHandlerTest, TestSystem) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), {})); + + // Snpahot + const SystemSnapshot* system = process_snapshot.System(); + ASSERT_NE(system, nullptr); +#if defined(ARCH_CPU_X86_64) + EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureX86_64); + EXPECT_STREQ(system->CPUVendor().c_str(), "GenuineIntel"); +#elif defined(ARCH_CPU_ARM64) + EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureARM64); + utsname uts; + ASSERT_EQ(uname(&uts), 0); + EXPECT_STREQ(system->MachineDescription().c_str(), uts.machine); +#else +#error Port to your CPU architecture +#endif + EXPECT_EQ(system->GetOperatingSystem(), SystemSnapshot::kOperatingSystemIOS); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestAnnotations) { + // This is “leaked” to crashpad_info. + crashpad::SimpleStringDictionary* simple_annotations = + new crashpad::SimpleStringDictionary(); + simple_annotations->SetKeyValue("#TEST# pad", "break"); + simple_annotations->SetKeyValue("#TEST# key", "value"); + simple_annotations->SetKeyValue("#TEST# pad", "crash"); + simple_annotations->SetKeyValue("#TEST# x", "y"); + simple_annotations->SetKeyValue("#TEST# longer", "shorter"); + simple_annotations->SetKeyValue("#TEST# empty_value", ""); + + crashpad::CrashpadInfo* crashpad_info = + crashpad::CrashpadInfo::GetCrashpadInfo(); + + crashpad_info->set_simple_annotations(simple_annotations); + + crashpad::AnnotationList::Register(); // This is “leaked” to crashpad_info. + + static crashpad::StringAnnotation<32> test_annotation_one{"#TEST# one"}; + static crashpad::StringAnnotation<32> test_annotation_two{"#TEST# two"}; + static crashpad::StringAnnotation<32> test_annotation_three{ + "#TEST# same-name"}; + static crashpad::StringAnnotation<32> test_annotation_four{ + "#TEST# same-name"}; + + test_annotation_one.Set("moocow"); + test_annotation_two.Set("this will be cleared"); + test_annotation_three.Set("same-name 3"); + test_annotation_four.Set("same-name 4"); + test_annotation_two.Clear(); + + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), {{"after_dump", "post"}})); + + auto process_map = process_snapshot.AnnotationsSimpleMap(); + EXPECT_EQ(process_map.size(), 1u); + EXPECT_EQ(process_map["after_dump"], "post"); + + std::map all_annotations_simple_map; + std::vector all_annotations; + for (const auto* module : process_snapshot.Modules()) { + std::map module_annotations_simple_map = + module->AnnotationsSimpleMap(); + all_annotations_simple_map.insert(module_annotations_simple_map.begin(), + module_annotations_simple_map.end()); + + std::vector annotations = module->AnnotationObjects(); + all_annotations.insert( + all_annotations.end(), annotations.begin(), annotations.end()); + } + + EXPECT_EQ(all_annotations_simple_map.size(), 5u); + EXPECT_EQ(all_annotations_simple_map["#TEST# pad"], "crash"); + EXPECT_EQ(all_annotations_simple_map["#TEST# key"], "value"); + EXPECT_EQ(all_annotations_simple_map["#TEST# x"], "y"); + EXPECT_EQ(all_annotations_simple_map["#TEST# longer"], "shorter"); + EXPECT_EQ(all_annotations_simple_map["#TEST# empty_value"], ""); + + bool saw_same_name_3 = false, saw_same_name_4 = false; + for (const auto& annotation : all_annotations) { + EXPECT_EQ(annotation.type, + static_cast(Annotation::Type::kString)); + std::string value(reinterpret_cast(annotation.value.data()), + annotation.value.size()); + if (annotation.name == "#TEST# one") { + EXPECT_EQ(value, "moocow"); + } else if (annotation.name == "#TEST# same-name") { + if (value == "same-name 3") { + EXPECT_FALSE(saw_same_name_3); + saw_same_name_3 = true; + } else if (value == "same-name 4") { + EXPECT_FALSE(saw_same_name_4); + saw_same_name_4 = true; + } else { + ADD_FAILURE() << "unexpected annotation value " << value; + } + } else { + ADD_FAILURE() << "unexpected annotation " << annotation.name; + } + } +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestThreads) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), {})); + + const auto& threads = process_snapshot.Threads(); + ASSERT_GT(threads.size(), 0u); + + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + ASSERT_EQ(thread_info(mach_thread_self(), + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count), + 0); + EXPECT_EQ(threads[0]->ThreadID(), identifier_info.thread_id); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestProcess) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), {})); + EXPECT_EQ(process_snapshot.ProcessID(), getpid()); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestMachException) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), {})); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestSignalException) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), {})); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestNSException) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), {})); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc b/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc index 0d240294..cd604510 100644 --- a/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc +++ b/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc @@ -156,20 +156,18 @@ bool ExceptionSnapshotIOSIntermediateDump::InitializeFromMachException( exception_ = exception; } - mach_msg_type_number_t code_count; - GetDataValueFromMap(exception_data, Key::kCodeCount, &code_count); - const IOSIntermediateDumpData* code_dump = - GetDataFromMap(exception_data, Key::kCode); + GetDataFromMap(exception_data, Key::kCodes); if (code_dump) { const std::vector& bytes = code_dump->bytes(); const mach_exception_data_type_t* code = reinterpret_cast(bytes.data()); - if (!code || - bytes.size() != sizeof(mach_exception_data_type_t) * code_count) { + if (bytes.size() == 0 || !code) { LOG(ERROR) << "Invalid mach exception code."; } else { // TODO: rationalize with the macOS implementation. + mach_msg_type_number_t code_count = + bytes.size() / sizeof(mach_exception_data_type_t); for (mach_msg_type_number_t code_index = 0; code_index < code_count; ++code_index) { codes_.push_back(code[code_index]); @@ -288,22 +286,24 @@ void ExceptionSnapshotIOSIntermediateDump::LoadContextFromThread( float_state_type float_state; debug_state_type debug_state; - mach_msg_type_number_t state_count = 0; thread_state_flavor_t flavor = THREAD_STATE_NONE; - if (GetDataValueFromMap(exception_data, Key::kStateCount, &state_count) && - GetDataValueFromMap(exception_data, Key::kFlavor, &flavor) && + if (GetDataValueFromMap(exception_data, Key::kFlavor, &flavor) && GetDataValueFromMap(other_thread, Key::kThreadState, &thread_state) && GetDataValueFromMap(other_thread, Key::kFloatState, &float_state) && GetDataValueFromMap(other_thread, Key::kDebugState, &debug_state)) { - size_t expected_length = ThreadStateLengthForFlavor(flavor); const IOSIntermediateDumpData* state_dump = GetDataFromMap(exception_data, Key::kState); if (state_dump) { const std::vector& bytes = state_dump->bytes(); size_t actual_length = bytes.size(); + size_t expected_length = ThreadStateLengthForFlavor(flavor); + // TODO(justincohen): Consider zero-ing out bytes if actual_length is + // shorter than expected_length, and tolerating actual_length longer than + // expected_length. if (expected_length == actual_length) { const ConstThreadState state = reinterpret_cast(bytes.data()); + mach_msg_type_number_t state_count = bytes.size() / sizeof(uint32_t); #if defined(ARCH_CPU_X86_64) InitializeCPUContextX86_64(&context_x86_64_, flavor, diff --git a/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc b/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc index 9710439a..0bc9247b 100644 --- a/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc +++ b/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc @@ -281,7 +281,6 @@ class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test { #if defined(ARCH_CPU_X86_64) thread_state_flavor_t flavor = x86_THREAD_STATE; - mach_msg_type_number_t state_count = x86_THREAD_STATE_COUNT; x86_thread_state_t state = {}; state.tsh.flavor = x86_THREAD_STATE64; state.tsh.count = x86_THREAD_STATE64_COUNT; @@ -289,7 +288,6 @@ class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test { size_t state_length = sizeof(x86_thread_state_t); #elif defined(ARCH_CPU_ARM64) thread_state_flavor_t flavor = ARM_UNIFIED_THREAD_STATE; - mach_msg_type_number_t state_count = ARM_UNIFIED_THREAD_STATE_COUNT; arm_unified_thread_state_t state = {}; state.ash.flavor = ARM_THREAD_STATE64; state.ash.count = ARM_THREAD_STATE64_COUNT; @@ -297,12 +295,10 @@ class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test { size_t state_length = sizeof(arm_unified_thread_state_t); #endif EXPECT_TRUE(writer->AddProperty(Key::kException, &exception)); - EXPECT_TRUE(writer->AddProperty(Key::kCode, code, code_count)); - EXPECT_TRUE(writer->AddProperty(Key::kCodeCount, &code_count)); + EXPECT_TRUE(writer->AddProperty(Key::kCodes, code, code_count)); EXPECT_TRUE(writer->AddProperty(Key::kFlavor, &flavor)); EXPECT_TRUE(writer->AddPropertyBytes( Key::kState, reinterpret_cast(&state), state_length)); - EXPECT_TRUE(writer->AddProperty(Key::kStateCount, &state_count)); uint64_t thread_id = 1; EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id)); } diff --git a/snapshot/mac/process_reader_mac.cc b/snapshot/mac/process_reader_mac.cc index 821fe3c0..9b2a2356 100644 --- a/snapshot/mac/process_reader_mac.cc +++ b/snapshot/mac/process_reader_mac.cc @@ -701,7 +701,7 @@ void ProcessReaderMac::LocateRedZone(mach_vm_address_t* const start_address, #if defined(ARCH_CPU_X86_FAMILY) if (Is64Bit()) { // x86_64 has a red zone. See AMD64 ABI 0.99.8, - // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-r252.pdf#page=19, + // https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/uploads/01de35b2c8adc7545de52604cc45d942/x86-64-psABI-2021-05-20.pdf#page=23. // section 3.2.2, “The Stack Frame”. constexpr mach_vm_size_t kRedZoneSize = 128; mach_vm_address_t red_zone_base = diff --git a/snapshot/mac/process_types/crashreporterclient.proctype b/snapshot/mac/process_types/crashreporterclient.proctype index 398bd639..226eaa25 100644 --- a/snapshot/mac/process_types/crashreporterclient.proctype +++ b/snapshot/mac/process_types/crashreporterclient.proctype @@ -23,7 +23,8 @@ // // This file is included by snapshot/mac/process_types.h and // snapshot/mac/process_types.cc to produce process type struct definitions and -// accessors. +// accessors. This file is also used by the iOS in process handler to read both +// messages in client/ios_handler/in_process_intermediate_dump_handler.cc. // Client Mach-O images will contain a __DATA,__crash_info section formatted // according to this structure. diff --git a/util/ios/ios_intermediate_dump_format.h b/util/ios/ios_intermediate_dump_format.h index 1d7cad65..46d3f467 100644 --- a/util/ios/ios_intermediate_dump_format.h +++ b/util/ios/ios_intermediate_dump_format.h @@ -26,12 +26,10 @@ namespace internal { TD(kInvalid, 0) \ TD(kVersion, 1) \ TD(kMachException, 1000) \ - TD(kCode, 1001) \ - TD(kCodeCount, 1002) \ - TD(kException, 1003) \ - TD(kFlavor, 1004) \ - TD(kState, 1005) \ - TD(kStateCount, 1006) \ + TD(kCodes, 1001) \ + TD(kException, 1002) \ + TD(kFlavor, 1003) \ + TD(kState, 1004) \ TD(kSignalException, 2000) \ TD(kSignalNumber, 2001) \ TD(kSignalCode, 2002) \ diff --git a/util/ios/ios_system_data_collector.h b/util/ios/ios_system_data_collector.h index 556d7242..cac0c869 100644 --- a/util/ios/ios_system_data_collector.h +++ b/util/ios/ios_system_data_collector.h @@ -29,16 +29,16 @@ class IOSSystemDataCollector { ~IOSSystemDataCollector(); void OSVersion(int* major, int* minor, int* bugfix) const; - std::string MachineDescription() const { return machine_description_; } + const std::string& MachineDescription() const { return machine_description_; } int ProcessorCount() const { return processor_count_; } - std::string Build() const { return build_; } - std::string CPUVendor() const { return cpu_vendor_; } + const std::string& Build() const { return build_; } + const std::string& CPUVendor() const { return cpu_vendor_; } bool HasDaylightSavingTime() const { return has_next_daylight_saving_time_; } bool IsDaylightSavingTime() const { return is_daylight_saving_time_; } int StandardOffsetSeconds() const { return standard_offset_seconds_; } int DaylightOffsetSeconds() const { return daylight_offset_seconds_; } - std::string StandardName() const { return standard_name_; } - std::string DaylightName() const { return daylight_name_; } + const std::string& StandardName() const { return standard_name_; } + const std::string& DaylightName() const { return daylight_name_; } // Currently unused by minidump. int Orientation() const { return orientation_; } diff --git a/util/ios/scoped_vm_read.cc b/util/ios/scoped_vm_read.cc index 3bbc5968..06cf4344 100644 --- a/util/ios/scoped_vm_read.cc +++ b/util/ios/scoped_vm_read.cc @@ -53,7 +53,7 @@ bool ScopedVMReadInternal::Read(const void* data, const size_t data_length) { data_ = vm_read_data_ + (data_address - page_region_address); return true; } else { - CRASHPAD_RAW_LOG_ERROR(kr, "vm_read"); + // It's expected that this will sometimes fail. Don't log here. return false; } } diff --git a/util/ios/scoped_vm_read.h b/util/ios/scoped_vm_read.h index 86d80393..3f396d45 100644 --- a/util/ios/scoped_vm_read.h +++ b/util/ios/scoped_vm_read.h @@ -70,13 +70,22 @@ class ScopedVMRead { //! \param[in] data Memory to be read by vm_read. //! \param[in] count Length of \a data. //! - //! \return `true` if all the data was read. Logs an error and returns false - //! on failure + //! \return `true` if all \a data was read. Returns false on failure. bool Read(const void* data, size_t count = 1) { size_t data_length = count * sizeof(T); return internal_.Read(data, data_length); } + //! \brief Releases any previously read data and vm_reads address. + //! + //! \param[in] address Address of memory to be read by vm_read. + //! \param[in] count Length of \a data. + //! + //! \return `true` if all of \a address was read. Returns false on failure. + bool Read(vm_address_t address, size_t count = 1) { + return Read(reinterpret_cast(address), count); + } + //! \brief Returns the pointer to memory safe to read during the in-process //! crash handler. T* operator->() const { return get(); }