toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial