• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
44namespace {
45
46const int kExceptionType = EXC_SOFTWARE;
47const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION;
48
49#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
50const uintptr_t kExpectedFinalFp = sizeof(uintptr_t);
51const uintptr_t kExpectedFinalSp = 0;
52
53// Append the given value to the sp position of the stack represented
54// by memory.
55void AppendToMemory(uint8_t *memory, uintptr_t sp, uintptr_t data) {
56  memcpy(memory + sp, &data, sizeof(data));
57}
58#endif
59
60}  // namespace
61
62namespace google_breakpad {
63
64IosExceptionMinidumpGenerator::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
74IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() {
75  [return_addresses_ release];
76}
77
78bool 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
91bool 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
109bool 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
126uintptr_t IosExceptionMinidumpGenerator::GetPCFromException() {
127  return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue];
128}
129
130uintptr_t IosExceptionMinidumpGenerator::GetLRFromException() {
131  return [[return_addresses_ objectAtIndex:1] unsignedIntegerValue];
132}
133
134bool 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
162bool 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