/ src / client / ios / exception_handler_no_mach.cc
exception_handler_no_mach.cc
  1  // Copyright 2006 Google LLC
  2  //
  3  // Redistribution and use in source and binary forms, with or without
  4  // modification, are permitted provided that the following conditions are
  5  // met:
  6  //
  7  //     * Redistributions of source code must retain the above copyright
  8  // notice, this list of conditions and the following disclaimer.
  9  //     * Redistributions in binary form must reproduce the above
 10  // copyright notice, this list of conditions and the following disclaimer
 11  // in the documentation and/or other materials provided with the
 12  // distribution.
 13  //     * Neither the name of Google LLC nor the names of its
 14  // contributors may be used to endorse or promote products derived from
 15  // this software without specific prior written permission.
 16  //
 17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  
 29  #ifdef HAVE_CONFIG_H
 30  #include <config.h>  // Must come first
 31  #endif
 32  
 33  #include <signal.h>
 34  #include <TargetConditionals.h>
 35  
 36  #include "client/mac/handler/minidump_generator.h"
 37  #include "client/ios/exception_handler_no_mach.h"
 38  
 39  #ifndef USE_PROTECTED_ALLOCATIONS
 40  #if TARGET_OS_TV
 41  #define USE_PROTECTED_ALLOCATIONS 1
 42  #else
 43  #define USE_PROTECTED_ALLOCATIONS 0
 44  #endif
 45  #endif
 46  
 47  // If USE_PROTECTED_ALLOCATIONS is activated then the
 48  // gBreakpadAllocator needs to be setup in other code
 49  // ahead of time.  Please see ProtectedMemoryAllocator.h
 50  // for more details.
 51  #if USE_PROTECTED_ALLOCATIONS
 52    #include "client/mac/handler/protected_memory_allocator.h"
 53    extern ProtectedMemoryAllocator* gBreakpadAllocator;
 54  #endif
 55  
 56  namespace google_breakpad {
 57  
 58  const int kExceptionSignals[] = {
 59     // Core-generating signals.
 60    SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGEMT,
 61    SIGXCPU, SIGXFSZ,
 62    // Non-core-generating but terminating signals.
 63    SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGPROF, SIGTERM, SIGUSR1, SIGUSR2,
 64    SIGVTALRM, SIGXCPU, SIGXFSZ, SIGIO,
 65  };
 66  const int kNumHandledSignals =
 67      sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
 68  struct scoped_ptr<struct sigaction> old_handlers[kNumHandledSignals];
 69  
 70  static union {
 71  #if USE_PROTECTED_ALLOCATIONS
 72  #if defined PAGE_MAX_SIZE
 73    char protected_buffer[PAGE_MAX_SIZE] __attribute__((aligned(PAGE_MAX_SIZE)));
 74  #else
 75    char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
 76  #endif  // defined PAGE_MAX_SIZE
 77  #endif  // USE_PROTECTED_ALLOCATIONS
 78    google_breakpad::ExceptionHandler* handler;
 79  } gProtectedData;
 80  
 81  ExceptionHandler::ExceptionHandler(const string& dump_path,
 82                                     FilterCallback filter,
 83                                     MinidumpCallback callback,
 84                                     void* callback_context,
 85                                     bool install_handler,
 86                                     const char* port_name)
 87      : dump_path_(),
 88        filter_(filter),
 89        callback_(callback),
 90        callback_context_(callback_context),
 91        directCallback_(NULL),
 92        installed_exception_handler_(false),
 93        is_in_teardown_(false) {
 94    // This will update to the ID and C-string pointers
 95    set_dump_path(dump_path);
 96    MinidumpGenerator::GatherSystemInformation();
 97    Setup();
 98  }
 99  
100  // special constructor if we want to bypass minidump writing and
101  // simply get a callback with the exception information
102  ExceptionHandler::ExceptionHandler(DirectCallback callback,
103                                     void* callback_context,
104                                     bool install_handler)
105      : dump_path_(),
106        filter_(NULL),
107        callback_(NULL),
108        callback_context_(callback_context),
109        directCallback_(callback),
110        installed_exception_handler_(false),
111        is_in_teardown_(false) {
112    MinidumpGenerator::GatherSystemInformation();
113    Setup();
114  }
115  
116  ExceptionHandler::~ExceptionHandler() {
117    Teardown();
118  }
119  
120  bool ExceptionHandler::WriteMinidumpWithException(
121      int exception_type,
122      int exception_code,
123      int exception_subcode,
124      breakpad_ucontext_t* task_context,
125      mach_port_t thread_name,
126      bool exit_after_write,
127      bool report_current_thread) {
128    bool result = false;
129  
130  #if !TARGET_OS_TV
131    exit_after_write = false;
132  #endif  // !TARGET_OS_TV
133  
134    if (directCallback_) {
135      if (directCallback_(callback_context_,
136                          exception_type,
137                          exception_code,
138                          exception_subcode,
139                          thread_name) ) {
140        if (exit_after_write)
141          _exit(exception_type);
142      }
143    } else {
144      string minidump_id;
145  
146      // Putting the MinidumpGenerator in its own context will ensure that the
147      // destructor is executed, closing the newly created minidump file.
148      if (!dump_path_.empty()) {
149        MinidumpGenerator md(mach_task_self(),
150                             report_current_thread ? MACH_PORT_NULL :
151                                                     mach_thread_self());
152        md.SetTaskContext(task_context);
153        if (exception_type && exception_code) {
154          // If this is a real exception, give the filter (if any) a chance to
155          // decide if this should be sent.
156          if (filter_ && !filter_(callback_context_))
157            return false;
158  
159          md.SetExceptionInformation(exception_type, exception_code,
160                                     exception_subcode, thread_name);
161        }
162  
163        result = md.Write(next_minidump_path_c_);
164      }
165  
166      // Call user specified callback (if any)
167      if (callback_) {
168        // If the user callback returned true and we're handling an exception
169        // (rather than just writing out the file), then we should exit without
170        // forwarding the exception to the next handler.
171        if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
172                      result)) {
173          if (exit_after_write)
174            _exit(exception_type);
175        }
176      }
177    }
178  
179    return result;
180  }
181  
182  // static
183  void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
184  #if USE_PROTECTED_ALLOCATIONS
185    if (gBreakpadAllocator)
186      gBreakpadAllocator->Unprotect();
187  #endif
188    gProtectedData.handler->WriteMinidumpWithException(
189        EXC_SOFTWARE,
190        MD_EXCEPTION_CODE_MAC_ABORT,
191        0,
192        static_cast<breakpad_ucontext_t*>(uc),
193        mach_thread_self(),
194        true,
195        true);
196  #if USE_PROTECTED_ALLOCATIONS
197    if (gBreakpadAllocator)
198      gBreakpadAllocator->Protect();
199  #endif
200  }
201  
202  bool ExceptionHandler::InstallHandlers() {
203    // If a handler is already installed, something is really wrong.
204    if (gProtectedData.handler != NULL)
205      return false;
206    for (int i = 0; i < kNumHandledSignals; ++i) {
207      struct sigaction sa;
208      memset(&sa, 0, sizeof(sa));
209      sigemptyset(&sa.sa_mask);
210      sigaddset(&sa.sa_mask, kExceptionSignals[i]);
211      sa.sa_sigaction = ExceptionHandler::SignalHandler;
212      sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
213  
214      if (sigaction(kExceptionSignals[i], &sa, old_handlers[i].get()) == -1) {
215        return false;
216      }
217    }
218    gProtectedData.handler = this;
219  #if USE_PROTECTED_ALLOCATIONS
220    assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
221    mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
222  #endif  // USE_PROTECTED_ALLOCATIONS
223    installed_exception_handler_ = true;
224    return true;
225  }
226  
227  bool ExceptionHandler::UninstallHandlers() {
228    for (int i = 0; i < kNumHandledSignals; ++i) {
229      if (old_handlers[i].get()) {
230        sigaction(kExceptionSignals[i], old_handlers[i].get(), NULL);
231        old_handlers[i].reset();
232      }
233    }
234  #if USE_PROTECTED_ALLOCATIONS
235    mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ | PROT_WRITE);
236  #endif  // USE_PROTECTED_ALLOCATIONS
237    gProtectedData.handler = NULL;
238    installed_exception_handler_ = false;
239    return true;
240  }
241  
242  bool ExceptionHandler::Setup() {
243    if (!InstallHandlers())
244      return false;
245    return true;
246  }
247  
248  bool ExceptionHandler::Teardown() {
249    is_in_teardown_ = true;
250  
251    if (!UninstallHandlers())
252      return false;
253  
254    return true;
255  }
256  
257  void ExceptionHandler::UpdateNextID() {
258    next_minidump_path_ =
259      (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
260  
261    next_minidump_path_c_ = next_minidump_path_.c_str();
262    next_minidump_id_c_ = next_minidump_id_.c_str();
263  }
264  
265  }  // namespace google_breakpad