Home | History | Annotate | Download | only in Framework
      1 // Copyright (c) 2006, 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 
     31 
     32 #define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
     33 
     34 #import "client/mac/Framework/Breakpad.h"
     35 
     36 #include <assert.h>
     37 #import <Foundation/Foundation.h>
     38 #include <pthread.h>
     39 #include <sys/stat.h>
     40 #include <sys/sysctl.h>
     41 
     42 #import "client/mac/crash_generation/Inspector.h"
     43 #import "client/mac/handler/exception_handler.h"
     44 #import "client/mac/Framework/Breakpad.h"
     45 #import "client/mac/Framework/OnDemandServer.h"
     46 #import "client/mac/handler/protected_memory_allocator.h"
     47 #include "common/mac/launch_reporter.h"
     48 #import "common/mac/MachIPC.h"
     49 #import "common/simple_string_dictionary.h"
     50 
     51 #if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions))
     52 // This file uses C++ try/catch (but shouldn't). Duplicate the macros from
     53 // <c++/4.2.1/exception_defines.h> allowing this file to work properly with
     54 // exceptions disabled even when other C++ libraries are used. #undef the try
     55 // and catch macros first in case libstdc++ is in use and has already provided
     56 // its own definitions.
     57 #undef try
     58 #define try       if (true)
     59 #undef catch
     60 #define catch(X)  if (false)
     61 #endif  // __EXCEPTIONS
     62 
     63 using google_breakpad::MachPortSender;
     64 using google_breakpad::MachReceiveMessage;
     65 using google_breakpad::MachSendMessage;
     66 using google_breakpad::ReceivePort;
     67 using google_breakpad::SimpleStringDictionary;
     68 
     69 //=============================================================================
     70 // We want any memory allocations which are used by breakpad during the
     71 // exception handling process (after a crash has happened) to be read-only
     72 // to prevent them from being smashed before a crash occurs.  Unfortunately
     73 // we cannot protect against smashes to our exception handling thread's
     74 // stack.
     75 //
     76 // NOTE: Any memory allocations which are not used during the exception
     77 // handling process may be allocated in the normal ways.
     78 //
     79 // The ProtectedMemoryAllocator class provides an Allocate() method which
     80 // we'll using in conjunction with placement operator new() to control
     81 // allocation of C++ objects.  Note that we don't use operator delete()
     82 // but instead call the objects destructor directly:  object->~ClassName();
     83 //
     84 ProtectedMemoryAllocator *gMasterAllocator = NULL;
     85 ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
     86 ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
     87 
     88 // Mutex for thread-safe access to the key/value dictionary used by breakpad.
     89 // It's a global instead of an instance variable of Breakpad
     90 // since it can't live in a protected memory area.
     91 pthread_mutex_t gDictionaryMutex;
     92 
     93 //=============================================================================
     94 // Stack-based object for thread-safe access to a memory-protected region.
     95 // It's assumed that normally the memory block (allocated by the allocator)
     96 // is protected (read-only).  Creating a stack-based instance of
     97 // ProtectedMemoryLocker will unprotect this block after taking the lock.
     98 // Its destructor will first re-protect the memory then release the lock.
     99 class ProtectedMemoryLocker {
    100  public:
    101   ProtectedMemoryLocker(pthread_mutex_t *mutex,
    102                         ProtectedMemoryAllocator *allocator)
    103       : mutex_(mutex),
    104         allocator_(allocator) {
    105     // Lock the mutex
    106     __attribute__((unused)) int rv = pthread_mutex_lock(mutex_);
    107     assert(rv == 0);
    108 
    109     // Unprotect the memory
    110     allocator_->Unprotect();
    111   }
    112 
    113   ~ProtectedMemoryLocker() {
    114     // First protect the memory
    115     allocator_->Protect();
    116 
    117     // Then unlock the mutex
    118     __attribute__((unused)) int rv = pthread_mutex_unlock(mutex_);
    119     assert(rv == 0);
    120   };
    121 
    122  private:
    123   ProtectedMemoryLocker();
    124   ProtectedMemoryLocker(const ProtectedMemoryLocker&);
    125   ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&);
    126 
    127   pthread_mutex_t           *mutex_;
    128   ProtectedMemoryAllocator  *allocator_;
    129 };
    130 
    131 //=============================================================================
    132 class Breakpad {
    133  public:
    134   // factory method
    135   static Breakpad *Create(NSDictionary *parameters) {
    136     // Allocate from our special allocation pool
    137     Breakpad *breakpad =
    138       new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
    139         Breakpad();
    140 
    141     if (!breakpad)
    142       return NULL;
    143 
    144     if (!breakpad->Initialize(parameters)) {
    145       // Don't use operator delete() here since we allocated from special pool
    146       breakpad->~Breakpad();
    147       return NULL;
    148     }
    149 
    150     return breakpad;
    151   }
    152 
    153   ~Breakpad();
    154 
    155   void SetKeyValue(NSString *key, NSString *value);
    156   NSString *KeyValue(NSString *key);
    157   void RemoveKeyValue(NSString *key);
    158 
    159   void GenerateAndSendReport();
    160 
    161   void SetFilterCallback(BreakpadFilterCallback callback, void *context) {
    162     filter_callback_ = callback;
    163     filter_callback_context_ = context;
    164   }
    165 
    166  private:
    167   Breakpad()
    168     : handler_(NULL),
    169       config_params_(NULL),
    170       send_and_exit_(true),
    171       filter_callback_(NULL),
    172       filter_callback_context_(NULL) {
    173     inspector_path_[0] = 0;
    174   }
    175 
    176   bool Initialize(NSDictionary *parameters);
    177   bool InitializeInProcess(NSDictionary *parameters);
    178   bool InitializeOutOfProcess(NSDictionary *parameters);
    179 
    180   bool ExtractParameters(NSDictionary *parameters);
    181 
    182   // Dispatches to HandleException()
    183   static bool ExceptionHandlerDirectCallback(void *context,
    184                                              int exception_type,
    185                                              int exception_code,
    186                                              int exception_subcode,
    187                                              mach_port_t crashing_thread);
    188 
    189   bool HandleException(int exception_type,
    190                        int exception_code,
    191                        int exception_subcode,
    192                        mach_port_t crashing_thread);
    193 
    194   // Dispatches to HandleMinidump().
    195   // This gets called instead of ExceptionHandlerDirectCallback when running
    196   // with the BREAKPAD_IN_PROCESS option.
    197   static bool HandleMinidumpCallback(const char *dump_dir,
    198                                      const char *minidump_id,
    199                                      void *context,
    200                                      bool succeeded);
    201 
    202   // This is only used when BREAKPAD_IN_PROCESS is YES.
    203   bool HandleMinidump(const char *dump_dir, const char *minidump_id);
    204 
    205   // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
    206   // MachineExceptions.h, we have to explicitly name the handler.
    207   google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
    208 
    209   char                    inspector_path_[PATH_MAX];  // Path to inspector tool
    210 
    211   SimpleStringDictionary  *config_params_; // Create parameters (STRONG)
    212 
    213   OnDemandServer          inspector_;
    214 
    215   bool                    send_and_exit_;  // Exit after sending, if true
    216 
    217   BreakpadFilterCallback  filter_callback_;
    218   void                    *filter_callback_context_;
    219 };
    220 
    221 #pragma mark -
    222 #pragma mark Helper functions
    223 
    224 //=============================================================================
    225 // Helper functions
    226 
    227 //=============================================================================
    228 static BOOL IsDebuggerActive() {
    229   BOOL result = NO;
    230   NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
    231 
    232   // We check both defaults and the environment variable here
    233 
    234   BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
    235 
    236   if (!ignoreDebugger) {
    237     char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
    238     ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
    239   }
    240 
    241   if (!ignoreDebugger) {
    242     pid_t pid = getpid();
    243     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    244     int mibSize = sizeof(mib) / sizeof(int);
    245     size_t actualSize;
    246 
    247     if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
    248       struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
    249 
    250       if (info) {
    251         // This comes from looking at the Darwin xnu Kernel
    252         if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
    253           result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
    254 
    255         free(info);
    256       }
    257     }
    258   }
    259 
    260   return result;
    261 }
    262 
    263 //=============================================================================
    264 bool Breakpad::ExceptionHandlerDirectCallback(void *context,
    265                                                     int exception_type,
    266                                                     int exception_code,
    267                                                     int exception_subcode,
    268                                                     mach_port_t crashing_thread) {
    269   Breakpad *breakpad = (Breakpad *)context;
    270 
    271   // If our context is damaged or something, just return false to indicate that
    272   // the handler should continue without us.
    273   if (!breakpad)
    274     return false;
    275 
    276   return breakpad->HandleException( exception_type,
    277                                     exception_code,
    278                                     exception_subcode,
    279                                     crashing_thread);
    280 }
    281 
    282 //=============================================================================
    283 bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
    284                                       const char *minidump_id,
    285                                       void *context,
    286                                       bool succeeded) {
    287   Breakpad *breakpad = (Breakpad *)context;
    288 
    289   // If our context is damaged or something, just return false to indicate that
    290   // the handler should continue without us.
    291   if (!breakpad || !succeeded)
    292     return false;
    293 
    294   return breakpad->HandleMinidump(dump_dir, minidump_id);
    295 }
    296 
    297 //=============================================================================
    298 #pragma mark -
    299 
    300 #include <dlfcn.h>
    301 
    302 //=============================================================================
    303 // Returns the pathname to the Resources directory for this version of
    304 // Breakpad which we are now running.
    305 //
    306 // Don't make the function static, since _dyld_lookup_and_bind_fully needs a
    307 // simple non-static C name
    308 //
    309 extern "C" {
    310 NSString * GetResourcePath();
    311 NSString * GetResourcePath() {
    312   NSString *resourcePath = nil;
    313 
    314   // If there are multiple breakpads installed then calling bundleWithIdentifier
    315   // will not work properly, so only use that as a backup plan.
    316   // We want to find the bundle containing the code where this function lives
    317   // and work from there
    318   //
    319 
    320   // Get the pathname to the code which contains this function
    321   Dl_info info;
    322   if (dladdr((const void*)GetResourcePath, &info) != 0) {
    323     NSFileManager *filemgr = [NSFileManager defaultManager];
    324     NSString *filePath =
    325         [filemgr stringWithFileSystemRepresentation:info.dli_fname
    326                                              length:strlen(info.dli_fname)];
    327     NSString *bundlePath = [filePath stringByDeletingLastPathComponent];
    328     // The "Resources" directory should be in the same directory as the
    329     // executable code, since that's how the Breakpad framework is built.
    330     resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"];
    331   } else {
    332     // fallback plan
    333     NSBundle *bundle =
    334         [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
    335     resourcePath = [bundle resourcePath];
    336   }
    337 
    338   return resourcePath;
    339 }
    340 }  // extern "C"
    341 
    342 //=============================================================================
    343 bool Breakpad::Initialize(NSDictionary *parameters) {
    344   // Initialize
    345   config_params_ = NULL;
    346   handler_ = NULL;
    347 
    348   // Check for debugger
    349   if (IsDebuggerActive()) {
    350     return true;
    351   }
    352 
    353   // Gather any user specified parameters
    354   if (!ExtractParameters(parameters)) {
    355     return false;
    356   }
    357 
    358   if ([[parameters objectForKey:@BREAKPAD_IN_PROCESS] boolValue])
    359     return InitializeInProcess(parameters);
    360   else
    361     return InitializeOutOfProcess(parameters);
    362 }
    363 
    364 //=============================================================================
    365 bool Breakpad::InitializeInProcess(NSDictionary* parameters) {
    366   handler_ =
    367       new (gBreakpadAllocator->Allocate(
    368           sizeof(google_breakpad::ExceptionHandler)))
    369           google_breakpad::ExceptionHandler(
    370               config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
    371               0, &HandleMinidumpCallback, this, true, 0);
    372   return true;    
    373 }
    374 
    375 //=============================================================================
    376 bool Breakpad::InitializeOutOfProcess(NSDictionary* parameters) {
    377   // Get path to Inspector executable.
    378   NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION);
    379 
    380   // Standardize path (resolve symlinkes, etc.)  and escape spaces
    381   inspectorPathString = [inspectorPathString stringByStandardizingPath];
    382   inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "]
    383                                               componentsJoinedByString:@"\\ "];
    384 
    385   // Create an on-demand server object representing the Inspector.
    386   // In case of a crash, we simply need to call the LaunchOnDemand()
    387   // method on it, then send a mach message to its service port.
    388   // It will then launch and perform a process inspection of our crashed state.
    389   // See the HandleException() method for the details.
    390 #define RECEIVE_PORT_NAME "com.Breakpad.Inspector"
    391 
    392   name_t portName;
    393   snprintf(portName, sizeof(name_t),  "%s%d", RECEIVE_PORT_NAME, getpid());
    394 
    395   // Save the location of the Inspector
    396   strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation],
    397           sizeof(inspector_path_));
    398 
    399   // Append a single command-line argument to the Inspector path
    400   // representing the bootstrap name of the launch-on-demand receive port.
    401   // When the Inspector is launched, it can use this to lookup the port
    402   // by calling bootstrap_check_in().
    403   strlcat(inspector_path_, " ", sizeof(inspector_path_));
    404   strlcat(inspector_path_, portName, sizeof(inspector_path_));
    405 
    406   kern_return_t kr = inspector_.Initialize(inspector_path_,
    407                                            portName,
    408                                            true);        // shutdown on exit
    409 
    410   if (kr != KERN_SUCCESS) {
    411     return false;
    412   }
    413 
    414   // Create the handler (allocating it in our special protected pool)
    415   handler_ =
    416       new (gBreakpadAllocator->Allocate(
    417           sizeof(google_breakpad::ExceptionHandler)))
    418           google_breakpad::ExceptionHandler(
    419               Breakpad::ExceptionHandlerDirectCallback, this, true);
    420   return true;
    421 }
    422 
    423 //=============================================================================
    424 Breakpad::~Breakpad() {
    425   // Note that we don't use operator delete() on these pointers,
    426   // since they were allocated by ProtectedMemoryAllocator objects.
    427   //
    428   if (config_params_) {
    429     config_params_->~SimpleStringDictionary();
    430   }
    431 
    432   if (handler_)
    433     handler_->~ExceptionHandler();
    434 }
    435 
    436 //=============================================================================
    437 bool Breakpad::ExtractParameters(NSDictionary *parameters) {
    438   NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
    439   NSString *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM];
    440   NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT];
    441 
    442   NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
    443   NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
    444   NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
    445   NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
    446   NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
    447   NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
    448   NSString *inspectorPathString =
    449       [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
    450   NSString *reporterPathString =
    451       [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
    452   NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
    453   NSArray  *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
    454   NSString *logFileTailSize =
    455       [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
    456   NSString *requestUserText =
    457       [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
    458   NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
    459   NSString *vendor =
    460       [parameters objectForKey:@BREAKPAD_VENDOR];
    461   NSString *dumpSubdirectory =
    462       [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
    463 
    464   NSDictionary *serverParameters =
    465       [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
    466 
    467   // These may have been set above as user prefs, which take priority.
    468   if (!skipConfirm) {
    469     skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
    470   }
    471   if (!sendAndExit) {
    472     sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
    473   }
    474 
    475   if (!product)
    476     product = [parameters objectForKey:@"CFBundleName"];
    477 
    478   if (!display) {
    479     display = [parameters objectForKey:@"CFBundleDisplayName"];
    480     if (!display) {
    481       display = product;
    482     }
    483   }
    484 
    485   if (!version)
    486     version = [parameters objectForKey:@"CFBundleVersion"];
    487 
    488   if (!interval)
    489     interval = @"3600";
    490 
    491   if (!timeout)
    492     timeout = @"300";
    493 
    494   if (!logFileTailSize)
    495     logFileTailSize = @"200000";
    496 
    497   if (!vendor) {
    498     vendor = @"Vendor not specified";
    499   }
    500 
    501   // Normalize the values.
    502   if (skipConfirm) {
    503     skipConfirm = [skipConfirm uppercaseString];
    504 
    505     if ([skipConfirm isEqualToString:@"YES"] ||
    506         [skipConfirm isEqualToString:@"TRUE"] ||
    507         [skipConfirm isEqualToString:@"1"])
    508       skipConfirm = @"YES";
    509     else
    510       skipConfirm = @"NO";
    511   } else {
    512     skipConfirm = @"NO";
    513   }
    514 
    515   send_and_exit_ = true;
    516   if (sendAndExit) {
    517     sendAndExit = [sendAndExit uppercaseString];
    518 
    519     if ([sendAndExit isEqualToString:@"NO"] ||
    520         [sendAndExit isEqualToString:@"FALSE"] ||
    521         [sendAndExit isEqualToString:@"0"])
    522       send_and_exit_ = false;
    523   }
    524 
    525   if (requestUserText) {
    526     requestUserText = [requestUserText uppercaseString];
    527 
    528     if ([requestUserText isEqualToString:@"YES"] ||
    529         [requestUserText isEqualToString:@"TRUE"] ||
    530         [requestUserText isEqualToString:@"1"])
    531       requestUserText = @"YES";
    532     else
    533       requestUserText = @"NO";
    534   } else {
    535     requestUserText = @"NO";
    536   }
    537 
    538   // Find the helper applications if not specified in user config.
    539   NSString *resourcePath = nil;
    540   if (!inspectorPathString || !reporterPathString) {
    541     resourcePath = GetResourcePath();
    542     if (!resourcePath) {
    543       return false;
    544     }
    545   }
    546 
    547   // Find Inspector.
    548   if (!inspectorPathString) {
    549     inspectorPathString =
    550         [resourcePath stringByAppendingPathComponent:@"Inspector"];
    551   }
    552 
    553   // Verify that there is an Inspector tool.
    554   if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
    555     return false;
    556   }
    557 
    558   // Find Reporter.
    559   if (!reporterPathString) {
    560     reporterPathString =
    561         [resourcePath
    562          stringByAppendingPathComponent:@"crash_report_sender.app"];
    563     reporterPathString =
    564         [[NSBundle bundleWithPath:reporterPathString] executablePath];
    565   }
    566 
    567   // Verify that there is a Reporter application.
    568   if (![[NSFileManager defaultManager]
    569              fileExistsAtPath:reporterPathString]) {
    570     return false;
    571   }
    572 
    573   if (!dumpSubdirectory) {
    574     dumpSubdirectory = @"";
    575   }
    576 
    577   // The product, version, and URL are required values.
    578   if (![product length]) {
    579     return false;
    580   }
    581 
    582   if (![version length]) {
    583     return false;
    584   }
    585 
    586   if (![urlStr length]) {
    587     return false;
    588   }
    589 
    590   config_params_ =
    591       new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
    592         SimpleStringDictionary();
    593 
    594   SimpleStringDictionary &dictionary = *config_params_;
    595 
    596   dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE,     [serverType UTF8String]);
    597   dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
    598   dictionary.SetKeyValue(BREAKPAD_PRODUCT,         [product UTF8String]);
    599   dictionary.SetKeyValue(BREAKPAD_VERSION,         [version UTF8String]);
    600   dictionary.SetKeyValue(BREAKPAD_URL,             [urlStr UTF8String]);
    601   dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]);
    602   dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM,    [skipConfirm UTF8String]);
    603   dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]);
    604   dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION,
    605                          [inspectorPathString fileSystemRepresentation]);
    606   dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION,
    607                          [reporterPathString fileSystemRepresentation]);
    608   dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE,
    609                          [logFileTailSize UTF8String]);
    610   dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
    611                          [requestUserText UTF8String]);
    612   dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]);
    613   dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
    614   dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
    615                          [dumpSubdirectory UTF8String]);
    616 
    617   struct timeval tv;
    618   gettimeofday(&tv, NULL);
    619   char timeStartedString[32];
    620   sprintf(timeStartedString, "%zd", tv.tv_sec);
    621   dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME,
    622                          timeStartedString);
    623 
    624   if (logFilePaths) {
    625     char logFileKey[255];
    626     for(unsigned int i = 0; i < [logFilePaths count]; i++) {
    627       sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i);
    628       dictionary.SetKeyValue(logFileKey,
    629                              [[logFilePaths objectAtIndex:i]
    630                                fileSystemRepresentation]);
    631     }
    632   }
    633 
    634   if (serverParameters) {
    635     // For each key-value pair, call BreakpadAddUploadParameter()
    636     NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
    637     NSString *aParameter;
    638     while ((aParameter = [keyEnumerator nextObject])) {
    639       BreakpadAddUploadParameter(this, aParameter,
    640 				 [serverParameters objectForKey:aParameter]);
    641     }
    642   }
    643   return true;
    644 }
    645 
    646 //=============================================================================
    647 void Breakpad::SetKeyValue(NSString *key, NSString *value) {
    648   // We allow nil values. This is the same as removing the keyvalue.
    649   if (!config_params_ || !key)
    650     return;
    651 
    652   config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
    653 }
    654 
    655 //=============================================================================
    656 NSString *Breakpad::KeyValue(NSString *key) {
    657   if (!config_params_ || !key)
    658     return nil;
    659 
    660   const char *value = config_params_->GetValueForKey([key UTF8String]);
    661   return value ? [NSString stringWithUTF8String:value] : nil;
    662 }
    663 
    664 //=============================================================================
    665 void Breakpad::RemoveKeyValue(NSString *key) {
    666   if (!config_params_ || !key) return;
    667 
    668   config_params_->RemoveKey([key UTF8String]);
    669 }
    670 
    671 //=============================================================================
    672 void Breakpad::GenerateAndSendReport() {
    673   config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES");
    674   HandleException(0, 0, 0, mach_thread_self());
    675   config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO");
    676 }
    677 
    678 //=============================================================================
    679 bool Breakpad::HandleException(int exception_type,
    680                                int exception_code,
    681                                int exception_subcode,
    682                                mach_port_t crashing_thread) {
    683   if (filter_callback_) {
    684     bool should_handle = filter_callback_(exception_type,
    685                                           exception_code,
    686                                           crashing_thread,
    687                                           filter_callback_context_);
    688     if (!should_handle) return false;
    689   }
    690 
    691   // We need to reset the memory protections to be read/write,
    692   // since LaunchOnDemand() requires changing state.
    693   gBreakpadAllocator->Unprotect();
    694   // Configure the server to launch when we message the service port.
    695   // The reason we do this here, rather than at startup, is that we
    696   // can leak a bootstrap service entry if this method is called and
    697   // there never ends up being a crash.
    698   inspector_.LaunchOnDemand();
    699   gBreakpadAllocator->Protect();
    700 
    701   // The Inspector should send a message to this port to verify it
    702   // received our information and has finished the inspection.
    703   ReceivePort acknowledge_port;
    704 
    705   // Send initial information to the Inspector.
    706   MachSendMessage message(kMsgType_InspectorInitialInfo);
    707   message.AddDescriptor(mach_task_self());          // our task
    708   message.AddDescriptor(crashing_thread);           // crashing thread
    709   message.AddDescriptor(mach_thread_self());        // exception-handling thread
    710   message.AddDescriptor(acknowledge_port.GetPort());// message receive port
    711 
    712   InspectorInfo info;
    713   info.exception_type = exception_type;
    714   info.exception_code = exception_code;
    715   info.exception_subcode = exception_subcode;
    716   info.parameter_count = config_params_->GetCount();
    717   message.SetData(&info, sizeof(info));
    718 
    719   MachPortSender sender(inspector_.GetServicePort());
    720 
    721   kern_return_t result = sender.SendMessage(message, 2000);
    722 
    723   if (result == KERN_SUCCESS) {
    724     // Now, send a series of key-value pairs to the Inspector.
    725     const SimpleStringDictionary::Entry *entry = NULL;
    726     SimpleStringDictionary::Iterator iter(*config_params_);
    727 
    728     while ( (entry = iter.Next()) ) {
    729       KeyValueMessageData keyvalue_data(*entry);
    730 
    731       MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair);
    732       keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data));
    733 
    734       result = sender.SendMessage(keyvalue_message, 2000);
    735 
    736       if (result != KERN_SUCCESS) {
    737         break;
    738       }
    739     }
    740 
    741     if (result == KERN_SUCCESS) {
    742       // Wait for acknowledgement that the inspection has finished.
    743       MachReceiveMessage acknowledge_messsage;
    744       result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000);
    745     }
    746   }
    747 
    748 #if VERBOSE
    749   PRINT_MACH_RESULT(result, "Breakpad: SendMessage ");
    750   printf("Breakpad: Inspector service port = %#x\n",
    751     inspector_.GetServicePort());
    752 #endif
    753 
    754   // If we don't want any forwarding, return true here to indicate that we've
    755   // processed things as much as we want.
    756   if (send_and_exit_) return true;
    757 
    758   return false;
    759 }
    760 
    761 //=============================================================================
    762 bool Breakpad::HandleMinidump(const char *dump_dir, const char *minidump_id) {
    763   google_breakpad::ConfigFile config_file;
    764   config_file.WriteFile(dump_dir, config_params_, dump_dir, minidump_id);
    765   google_breakpad::LaunchReporter(
    766       config_params_->GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION),
    767       config_file.GetFilePath());
    768   return true;
    769 }
    770 
    771 //=============================================================================
    772 //=============================================================================
    773 
    774 #pragma mark -
    775 #pragma mark Public API
    776 
    777 //=============================================================================
    778 BreakpadRef BreakpadCreate(NSDictionary *parameters) {
    779   try {
    780     // This is confusing.  Our two main allocators for breakpad memory are:
    781     //    - gKeyValueAllocator for the key/value memory
    782     //    - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
    783     //      breakpad allocations which are accessed at exception handling time.
    784     //
    785     // But in order to avoid these two allocators themselves from being smashed,
    786     // we'll protect them as well by allocating them with gMasterAllocator.
    787     //
    788     // gMasterAllocator itself will NOT be protected, but this doesn't matter,
    789     // since once it does its allocations and locks the memory, smashes to itself
    790     // don't affect anything we care about.
    791     gMasterAllocator =
    792         new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
    793 
    794     gKeyValueAllocator =
    795         new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
    796             ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
    797 
    798     // Create a mutex for use in accessing the SimpleStringDictionary
    799     int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
    800     if (mutexResult == 0) {
    801 
    802       // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
    803       // Let's round up to the nearest page size.
    804       //
    805       int breakpad_pool_size = 4096;
    806 
    807       /*
    808        sizeof(Breakpad)
    809        + sizeof(google_breakpad::ExceptionHandler)
    810        + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
    811        */
    812 
    813       gBreakpadAllocator =
    814           new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
    815               ProtectedMemoryAllocator(breakpad_pool_size);
    816 
    817       // Stack-based autorelease pool for Breakpad::Create() obj-c code.
    818       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    819       Breakpad *breakpad = Breakpad::Create(parameters);
    820 
    821       if (breakpad) {
    822         // Make read-only to protect against memory smashers
    823         gMasterAllocator->Protect();
    824         gKeyValueAllocator->Protect();
    825         gBreakpadAllocator->Protect();
    826         // Can uncomment this line to figure out how much space was actually
    827         // allocated using this allocator
    828         //     printf("gBreakpadAllocator allocated size = %d\n",
    829         //         gBreakpadAllocator->GetAllocatedSize() );
    830         [pool release];
    831         return (BreakpadRef)breakpad;
    832       }
    833 
    834       [pool release];
    835     }
    836   } catch(...) {    // don't let exceptions leave this C API
    837     fprintf(stderr, "BreakpadCreate() : error\n");
    838   }
    839 
    840   if (gKeyValueAllocator) {
    841     gKeyValueAllocator->~ProtectedMemoryAllocator();
    842     gKeyValueAllocator = NULL;
    843   }
    844 
    845   if (gBreakpadAllocator) {
    846     gBreakpadAllocator->~ProtectedMemoryAllocator();
    847     gBreakpadAllocator = NULL;
    848   }
    849 
    850   delete gMasterAllocator;
    851   gMasterAllocator = NULL;
    852 
    853   return NULL;
    854 }
    855 
    856 //=============================================================================
    857 void BreakpadRelease(BreakpadRef ref) {
    858   try {
    859     Breakpad *breakpad = (Breakpad *)ref;
    860 
    861     if (gMasterAllocator) {
    862       gMasterAllocator->Unprotect();
    863       gKeyValueAllocator->Unprotect();
    864       gBreakpadAllocator->Unprotect();
    865 
    866       breakpad->~Breakpad();
    867 
    868       // Unfortunately, it's not possible to deallocate this stuff
    869       // because the exception handling thread is still finishing up
    870       // asynchronously at this point...  OK, it could be done with
    871       // locks, etc.  But since BreakpadRelease() should usually only
    872       // be called right before the process exits, it's not worth
    873       // deallocating this stuff.
    874 #if 0
    875       gKeyValueAllocator->~ProtectedMemoryAllocator();
    876       gBreakpadAllocator->~ProtectedMemoryAllocator();
    877       delete gMasterAllocator;
    878 
    879       gMasterAllocator = NULL;
    880       gKeyValueAllocator = NULL;
    881       gBreakpadAllocator = NULL;
    882 #endif
    883 
    884       pthread_mutex_destroy(&gDictionaryMutex);
    885     }
    886   } catch(...) {    // don't let exceptions leave this C API
    887     fprintf(stderr, "BreakpadRelease() : error\n");
    888   }
    889 }
    890 
    891 //=============================================================================
    892 void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
    893   try {
    894     // Not called at exception time
    895     Breakpad *breakpad = (Breakpad *)ref;
    896 
    897     if (breakpad && key && gKeyValueAllocator) {
    898       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    899 
    900       breakpad->SetKeyValue(key, value);
    901     }
    902   } catch(...) {    // don't let exceptions leave this C API
    903     fprintf(stderr, "BreakpadSetKeyValue() : error\n");
    904   }
    905 }
    906 
    907 void BreakpadAddUploadParameter(BreakpadRef ref,
    908                                 NSString *key,
    909                                 NSString *value) {
    910   // The only difference, internally, between an upload parameter and
    911   // a key value one that is set with BreakpadSetKeyValue is that we
    912   // prepend the keyname with a special prefix.  This informs the
    913   // crash sender that the parameter should be sent along with the
    914   // POST of the crash dump upload.
    915   try {
    916     Breakpad *breakpad = (Breakpad *)ref;
    917 
    918     if (breakpad && key && gKeyValueAllocator) {
    919       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    920 
    921       NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
    922 				stringByAppendingString:key];
    923       breakpad->SetKeyValue(prefixedKey, value);
    924     }
    925   } catch(...) {    // don't let exceptions leave this C API
    926     fprintf(stderr, "BreakpadSetKeyValue() : error\n");
    927   }
    928 }
    929 
    930 void BreakpadRemoveUploadParameter(BreakpadRef ref,
    931                                    NSString *key) {
    932   try {
    933     // Not called at exception time
    934     Breakpad *breakpad = (Breakpad *)ref;
    935 
    936     if (breakpad && key && gKeyValueAllocator) {
    937       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    938 
    939       NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
    940                                         @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
    941       breakpad->RemoveKeyValue(prefixedKey);
    942     }
    943   } catch(...) {    // don't let exceptions leave this C API
    944     fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
    945   }
    946 }
    947 //=============================================================================
    948 NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
    949   NSString *value = nil;
    950 
    951   try {
    952     // Not called at exception time
    953     Breakpad *breakpad = (Breakpad *)ref;
    954 
    955     if (!breakpad || !key || !gKeyValueAllocator)
    956       return nil;
    957 
    958     ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    959 
    960     value = breakpad->KeyValue(key);
    961   } catch(...) {    // don't let exceptions leave this C API
    962     fprintf(stderr, "BreakpadKeyValue() : error\n");
    963   }
    964 
    965   return value;
    966 }
    967 
    968 //=============================================================================
    969 void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
    970   try {
    971     // Not called at exception time
    972     Breakpad *breakpad = (Breakpad *)ref;
    973 
    974     if (breakpad && key && gKeyValueAllocator) {
    975       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    976 
    977       breakpad->RemoveKeyValue(key);
    978     }
    979   } catch(...) {    // don't let exceptions leave this C API
    980     fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
    981   }
    982 }
    983 
    984 //=============================================================================
    985 void BreakpadGenerateAndSendReport(BreakpadRef ref) {
    986   try {
    987     Breakpad *breakpad = (Breakpad *)ref;
    988 
    989     if (breakpad && gKeyValueAllocator) {
    990       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    991 
    992       gBreakpadAllocator->Unprotect();
    993       breakpad->GenerateAndSendReport();
    994       gBreakpadAllocator->Protect();
    995     }
    996   } catch(...) {    // don't let exceptions leave this C API
    997     fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
    998   }
    999 }
   1000 
   1001 //=============================================================================
   1002 void BreakpadSetFilterCallback(BreakpadRef ref,
   1003                                BreakpadFilterCallback callback,
   1004                                void *context) {
   1005 
   1006   try {
   1007     Breakpad *breakpad = (Breakpad *)ref;
   1008 
   1009     if (breakpad && gBreakpadAllocator) {
   1010       // share the dictionary mutex here (we really don't need a mutex)
   1011       ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator);
   1012 
   1013       breakpad->SetFilterCallback(callback, context);
   1014     }
   1015   } catch(...) {    // don't let exceptions leave this C API
   1016     fprintf(stderr, "BreakpadSetFilterCallback() : error\n");
   1017   }
   1018 }
   1019 
   1020 //============================================================================
   1021 void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) {
   1022   int logFileCounter = 0;
   1023 
   1024   NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
   1025                                    @BREAKPAD_LOGFILE_KEY_PREFIX,
   1026                                    logFileCounter];
   1027 
   1028   NSString *existingLogFilename = nil;
   1029   existingLogFilename = BreakpadKeyValue(ref, logFileKey);
   1030   // Find the first log file key that we can use by testing for existence
   1031   while (existingLogFilename) {
   1032     if ([existingLogFilename isEqualToString:logPathname]) {
   1033       return;
   1034     }
   1035     logFileCounter++;
   1036     logFileKey = [NSString stringWithFormat:@"%@%d",
   1037                            @BREAKPAD_LOGFILE_KEY_PREFIX,
   1038                            logFileCounter];
   1039     existingLogFilename = BreakpadKeyValue(ref, logFileKey);
   1040   }
   1041 
   1042   BreakpadSetKeyValue(ref, logFileKey, logPathname);
   1043 }
   1044