Home | History | Annotate | Download | only in handler
      1 // Copyright (c) 2012, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 #include "client/ios/handler/ios_exception_minidump_generator.h"
     31 
     32 #include <pthread.h>
     33 
     34 #include "google_breakpad/common/minidump_cpu_arm.h"
     35 #include "google_breakpad/common/minidump_cpu_arm64.h"
     36 #include "google_breakpad/common/minidump_exception_mac.h"
     37 #include "client/minidump_file_writer-inl.h"
     38 #include "common/scoped_ptr.h"
     39 
     40 #if defined(HAS_ARM_SUPPORT) && defined(HAS_ARM64_SUPPORT)
     41 #error "This file should be compiled for only one architecture at a time"
     42 #endif
     43 
     44 namespace {
     45 
     46 const int kExceptionType = EXC_SOFTWARE;
     47 const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION;
     48 
     49 #if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
     50 const uintptr_t kExpectedFinalFp = sizeof(uintptr_t);
     51 const uintptr_t kExpectedFinalSp = 0;
     52 
     53 // Append the given value to the sp position of the stack represented
     54 // by memory.
     55 void AppendToMemory(uint8_t *memory, uintptr_t sp, uintptr_t data) {
     56   memcpy(memory + sp, &data, sizeof(data));
     57 }
     58 #endif
     59 
     60 }  // namespace
     61 
     62 namespace google_breakpad {
     63 
     64 IosExceptionMinidumpGenerator::IosExceptionMinidumpGenerator(
     65     NSException *exception)
     66     : MinidumpGenerator(mach_task_self(), 0) {
     67   return_addresses_ = [[exception callStackReturnAddresses] retain];
     68   SetExceptionInformation(kExceptionType,
     69                           kExceptionCode,
     70                           0,
     71                           pthread_mach_thread_np(pthread_self()));
     72 }
     73 
     74 IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() {
     75   [return_addresses_ release];
     76 }
     77 
     78 bool IosExceptionMinidumpGenerator::WriteCrashingContext(
     79     MDLocationDescriptor *register_location) {
     80 #ifdef HAS_ARM_SUPPORT
     81   return WriteCrashingContextARM(register_location);
     82 #elif defined(HAS_ARM64_SUPPORT)
     83   return WriteCrashingContextARM64(register_location);
     84 #else
     85   assert(false);
     86   return false;
     87 #endif
     88 }
     89 
     90 #ifdef HAS_ARM_SUPPORT
     91 bool IosExceptionMinidumpGenerator::WriteCrashingContextARM(
     92     MDLocationDescriptor *register_location) {
     93   TypedMDRVA<MDRawContextARM> context(&writer_);
     94   if (!context.Allocate())
     95     return false;
     96   *register_location = context.location();
     97   MDRawContextARM *context_ptr = context.get();
     98   memset(context_ptr, 0, sizeof(MDRawContextARM));
     99   context_ptr->context_flags = MD_CONTEXT_ARM_FULL;
    100   context_ptr->iregs[MD_CONTEXT_ARM_REG_IOS_FP] = kExpectedFinalFp;  // FP
    101   context_ptr->iregs[MD_CONTEXT_ARM_REG_SP] = kExpectedFinalSp;      // SP
    102   context_ptr->iregs[MD_CONTEXT_ARM_REG_LR] = GetLRFromException();  // LR
    103   context_ptr->iregs[MD_CONTEXT_ARM_REG_PC] = GetPCFromException();  // PC
    104   return true;
    105 }
    106 #endif
    107 
    108 #ifdef HAS_ARM64_SUPPORT
    109 bool IosExceptionMinidumpGenerator::WriteCrashingContextARM64(
    110     MDLocationDescriptor *register_location) {
    111   TypedMDRVA<MDRawContextARM64> context(&writer_);
    112   if (!context.Allocate())
    113     return false;
    114   *register_location = context.location();
    115   MDRawContextARM64 *context_ptr = context.get();
    116   memset(context_ptr, 0, sizeof(*context_ptr));
    117   context_ptr->context_flags = MD_CONTEXT_ARM64_FULL;
    118   context_ptr->iregs[MD_CONTEXT_ARM64_REG_FP] = kExpectedFinalFp;      // FP
    119   context_ptr->iregs[MD_CONTEXT_ARM64_REG_SP] = kExpectedFinalSp;      // SP
    120   context_ptr->iregs[MD_CONTEXT_ARM64_REG_LR] = GetLRFromException();  // LR
    121   context_ptr->iregs[MD_CONTEXT_ARM64_REG_PC] = GetPCFromException();  // PC
    122   return true;
    123 }
    124 #endif
    125 
    126 uintptr_t IosExceptionMinidumpGenerator::GetPCFromException() {
    127   return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue];
    128 }
    129 
    130 uintptr_t IosExceptionMinidumpGenerator::GetLRFromException() {
    131   return [[return_addresses_ objectAtIndex:1] unsignedIntegerValue];
    132 }
    133 
    134 bool IosExceptionMinidumpGenerator::WriteExceptionStream(
    135     MDRawDirectory *exception_stream) {
    136 #if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
    137   TypedMDRVA<MDRawExceptionStream> exception(&writer_);
    138 
    139   if (!exception.Allocate())
    140     return false;
    141 
    142   exception_stream->stream_type = MD_EXCEPTION_STREAM;
    143   exception_stream->location = exception.location();
    144   MDRawExceptionStream *exception_ptr = exception.get();
    145   exception_ptr->thread_id = pthread_mach_thread_np(pthread_self());
    146 
    147   // This naming is confusing, but it is the proper translation from
    148   // mach naming to minidump naming.
    149   exception_ptr->exception_record.exception_code = kExceptionType;
    150   exception_ptr->exception_record.exception_flags = kExceptionCode;
    151 
    152   if (!WriteCrashingContext(&exception_ptr->thread_context))
    153     return false;
    154 
    155   exception_ptr->exception_record.exception_address = GetPCFromException();
    156   return true;
    157 #else
    158   return MinidumpGenerator::WriteExceptionStream(exception_stream);
    159 #endif
    160 }
    161 
    162 bool IosExceptionMinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
    163                                                       MDRawThread *thread) {
    164 #if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
    165   if (pthread_mach_thread_np(pthread_self()) != thread_id)
    166     return MinidumpGenerator::WriteThreadStream(thread_id, thread);
    167 
    168   size_t frame_count = [return_addresses_ count];
    169   if (frame_count == 0)
    170     return false;
    171   UntypedMDRVA memory(&writer_);
    172   size_t pointer_size = sizeof(uintptr_t);
    173   size_t frame_record_size = 2 * pointer_size;
    174   size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size;
    175   if (!memory.Allocate(stack_size))
    176     return false;
    177   scoped_array<uint8_t> stack_memory(new uint8_t[stack_size]);
    178   uintptr_t sp = stack_size - pointer_size;
    179   uintptr_t fp = 0;
    180   uintptr_t lr = 0;
    181   for (size_t current_frame = frame_count - 1;
    182        current_frame > 0;
    183        --current_frame) {
    184     AppendToMemory(stack_memory.get(), sp, lr);
    185     sp -= pointer_size;
    186     AppendToMemory(stack_memory.get(), sp, fp);
    187     fp = sp;
    188     sp -= pointer_size;
    189     lr = [[return_addresses_ objectAtIndex:current_frame] unsignedIntegerValue];
    190   }
    191   if (!memory.Copy(stack_memory.get(), stack_size))
    192     return false;
    193   assert(sp == kExpectedFinalSp);
    194   assert(fp == kExpectedFinalFp);
    195   assert(lr == GetLRFromException());
    196   thread->stack.start_of_memory_range = sp;
    197   thread->stack.memory = memory.location();
    198   memory_blocks_.push_back(thread->stack);
    199 
    200   if (!WriteCrashingContext(&thread->thread_context))
    201     return false;
    202 
    203   thread->thread_id = thread_id;
    204   return true;
    205 #else
    206   return MinidumpGenerator::WriteThreadStream(thread_id, thread);
    207 #endif
    208 }
    209 
    210 }  // namespace google_breakpad
    211