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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1008 @@
     1.4 +// Copyright (c) 2006, Google Inc.
     1.5 +// All rights reserved.
     1.6 +//
     1.7 +// Redistribution and use in source and binary forms, with or without
     1.8 +// modification, are permitted provided that the following conditions are
     1.9 +// met:
    1.10 +//
    1.11 +//     * Redistributions of source code must retain the above copyright
    1.12 +// notice, this list of conditions and the following disclaimer.
    1.13 +//     * Redistributions in binary form must reproduce the above
    1.14 +// copyright notice, this list of conditions and the following disclaimer
    1.15 +// in the documentation and/or other materials provided with the
    1.16 +// distribution.
    1.17 +//     * Neither the name of Google Inc. nor the names of its
    1.18 +// contributors may be used to endorse or promote products derived from
    1.19 +// this software without specific prior written permission.
    1.20 +//
    1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.32 +//
    1.33 +
    1.34 +#define VERBOSE 0
    1.35 +
    1.36 +#if VERBOSE
    1.37 +  static bool gDebugLog = true;
    1.38 +#else
    1.39 +  static bool gDebugLog = false;
    1.40 +#endif
    1.41 +
    1.42 +#define DEBUGLOG if (gDebugLog) fprintf
    1.43 +#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
    1.44 +
    1.45 +#import "client/mac/Framework/Breakpad.h"
    1.46 +
    1.47 +#import <Foundation/Foundation.h>
    1.48 +#include <pthread.h>
    1.49 +#include <sys/stat.h>
    1.50 +#include <sys/sysctl.h>
    1.51 +
    1.52 +#import "client/mac/crash_generation/Inspector.h"
    1.53 +#import "client/mac/handler/exception_handler.h"
    1.54 +#import "client/mac/Framework/Breakpad.h"
    1.55 +#import "client/mac/Framework/OnDemandServer.h"
    1.56 +#import "client/mac/handler/protected_memory_allocator.h"
    1.57 +#import "common/mac/MachIPC.h"
    1.58 +#import "common/mac/SimpleStringDictionary.h"
    1.59 +
    1.60 +#ifndef __EXCEPTIONS
    1.61 +// This file uses C++ try/catch (but shouldn't). Duplicate the macros from
    1.62 +// <c++/4.2.1/exception_defines.h> allowing this file to work properly with
    1.63 +// exceptions disabled even when other C++ libraries are used. #undef the try
    1.64 +// and catch macros first in case libstdc++ is in use and has already provided
    1.65 +// its own definitions.
    1.66 +#undef try
    1.67 +#define try       if (true)
    1.68 +#undef catch
    1.69 +#define catch(X)  if (false)
    1.70 +#endif  // __EXCEPTIONS
    1.71 +
    1.72 +using google_breakpad::KeyValueEntry;
    1.73 +using google_breakpad::MachPortSender;
    1.74 +using google_breakpad::MachReceiveMessage;
    1.75 +using google_breakpad::MachSendMessage;
    1.76 +using google_breakpad::ReceivePort;
    1.77 +using google_breakpad::SimpleStringDictionary;
    1.78 +using google_breakpad::SimpleStringDictionaryIterator;
    1.79 +
    1.80 +//=============================================================================
    1.81 +// We want any memory allocations which are used by breakpad during the
    1.82 +// exception handling process (after a crash has happened) to be read-only
    1.83 +// to prevent them from being smashed before a crash occurs.  Unfortunately
    1.84 +// we cannot protect against smashes to our exception handling thread's
    1.85 +// stack.
    1.86 +//
    1.87 +// NOTE: Any memory allocations which are not used during the exception
    1.88 +// handling process may be allocated in the normal ways.
    1.89 +//
    1.90 +// The ProtectedMemoryAllocator class provides an Allocate() method which
    1.91 +// we'll using in conjunction with placement operator new() to control
    1.92 +// allocation of C++ objects.  Note that we don't use operator delete()
    1.93 +// but instead call the objects destructor directly:  object->~ClassName();
    1.94 +//
    1.95 +ProtectedMemoryAllocator *gMasterAllocator = NULL;
    1.96 +ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
    1.97 +ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
    1.98 +
    1.99 +// Mutex for thread-safe access to the key/value dictionary used by breakpad.
   1.100 +// It's a global instead of an instance variable of Breakpad
   1.101 +// since it can't live in a protected memory area.
   1.102 +pthread_mutex_t gDictionaryMutex;
   1.103 +
   1.104 +//=============================================================================
   1.105 +// Stack-based object for thread-safe access to a memory-protected region.
   1.106 +// It's assumed that normally the memory block (allocated by the allocator)
   1.107 +// is protected (read-only).  Creating a stack-based instance of
   1.108 +// ProtectedMemoryLocker will unprotect this block after taking the lock.
   1.109 +// Its destructor will first re-protect the memory then release the lock.
   1.110 +class ProtectedMemoryLocker {
   1.111 +public:
   1.112 +  // allocator may be NULL, in which case no Protect() or Unprotect() calls
   1.113 +  // will be made, but a lock will still be taken
   1.114 +  ProtectedMemoryLocker(pthread_mutex_t *mutex,
   1.115 +                        ProtectedMemoryAllocator *allocator)
   1.116 +  : mutex_(mutex), allocator_(allocator) {
   1.117 +    // Lock the mutex
   1.118 +    assert(pthread_mutex_lock(mutex_) == 0);
   1.119 +
   1.120 +    // Unprotect the memory
   1.121 +    if (allocator_ ) {
   1.122 +      allocator_->Unprotect();
   1.123 +    }
   1.124 +  }
   1.125 +
   1.126 +  ~ProtectedMemoryLocker() {
   1.127 +    // First protect the memory
   1.128 +    if (allocator_) {
   1.129 +      allocator_->Protect();
   1.130 +    }
   1.131 +
   1.132 +    // Then unlock the mutex
   1.133 +    assert(pthread_mutex_unlock(mutex_) == 0);
   1.134 +  };
   1.135 +
   1.136 +private:
   1.137 +  //  Keep anybody from ever creating one of these things not on the stack.
   1.138 +  ProtectedMemoryLocker() { }
   1.139 +  ProtectedMemoryLocker(const ProtectedMemoryLocker&);
   1.140 +  ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&);
   1.141 +
   1.142 +  pthread_mutex_t           *mutex_;
   1.143 +  ProtectedMemoryAllocator  *allocator_;
   1.144 +};
   1.145 +
   1.146 +//=============================================================================
   1.147 +class Breakpad {
   1.148 + public:
   1.149 +  // factory method
   1.150 +  static Breakpad *Create(NSDictionary *parameters) {
   1.151 +    // Allocate from our special allocation pool
   1.152 +    Breakpad *breakpad =
   1.153 +      new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
   1.154 +        Breakpad();
   1.155 +
   1.156 +    if (!breakpad)
   1.157 +      return NULL;
   1.158 +
   1.159 +    if (!breakpad->Initialize(parameters)) {
   1.160 +      // Don't use operator delete() here since we allocated from special pool
   1.161 +      breakpad->~Breakpad();
   1.162 +      return NULL;
   1.163 +    }
   1.164 +
   1.165 +    return breakpad;
   1.166 +  }
   1.167 +
   1.168 +  ~Breakpad();
   1.169 +
   1.170 +  void SetKeyValue(NSString *key, NSString *value);
   1.171 +  NSString *KeyValue(NSString *key);
   1.172 +  void RemoveKeyValue(NSString *key);
   1.173 +
   1.174 +  void GenerateAndSendReport();
   1.175 +
   1.176 +  void SetFilterCallback(BreakpadFilterCallback callback, void *context) {
   1.177 +    filter_callback_ = callback;
   1.178 +    filter_callback_context_ = context;
   1.179 +  }
   1.180 +
   1.181 + private:
   1.182 +  Breakpad()
   1.183 +    : handler_(NULL),
   1.184 +      config_params_(NULL),
   1.185 +      send_and_exit_(true),
   1.186 +      filter_callback_(NULL),
   1.187 +      filter_callback_context_(NULL) {
   1.188 +    inspector_path_[0] = 0;
   1.189 +  }
   1.190 +
   1.191 +  bool Initialize(NSDictionary *parameters);
   1.192 +
   1.193 +  bool ExtractParameters(NSDictionary *parameters);
   1.194 +
   1.195 +  // Dispatches to HandleException()
   1.196 +  static bool ExceptionHandlerDirectCallback(void *context,
   1.197 +                                             int exception_type,
   1.198 +                                             int exception_code,
   1.199 +                                             int exception_subcode,
   1.200 +                                             mach_port_t crashing_thread);
   1.201 +
   1.202 +  bool HandleException(int exception_type,
   1.203 +                       int exception_code,
   1.204 +                       int exception_subcode,
   1.205 +                       mach_port_t crashing_thread);
   1.206 +
   1.207 +  // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
   1.208 +  // MachineExceptions.h, we have to explicitly name the handler.
   1.209 +  google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
   1.210 +
   1.211 +  char                    inspector_path_[PATH_MAX];  // Path to inspector tool
   1.212 +
   1.213 +  SimpleStringDictionary  *config_params_; // Create parameters (STRONG)
   1.214 +
   1.215 +  OnDemandServer          inspector_;
   1.216 +
   1.217 +  bool                    send_and_exit_;  // Exit after sending, if true
   1.218 +
   1.219 +  BreakpadFilterCallback  filter_callback_;
   1.220 +  void                    *filter_callback_context_;
   1.221 +};
   1.222 +
   1.223 +#pragma mark -
   1.224 +#pragma mark Helper functions
   1.225 +
   1.226 +//=============================================================================
   1.227 +// Helper functions
   1.228 +
   1.229 +//=============================================================================
   1.230 +static BOOL IsDebuggerActive() {
   1.231 +  BOOL result = NO;
   1.232 +  NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
   1.233 +
   1.234 +  // We check both defaults and the environment variable here
   1.235 +
   1.236 +  BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
   1.237 +
   1.238 +  if (!ignoreDebugger) {
   1.239 +    char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
   1.240 +    ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
   1.241 +  }
   1.242 +
   1.243 +  if (!ignoreDebugger) {
   1.244 +    pid_t pid = getpid();
   1.245 +    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
   1.246 +    int mibSize = sizeof(mib) / sizeof(int);
   1.247 +    size_t actualSize;
   1.248 +
   1.249 +    if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
   1.250 +      struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
   1.251 +
   1.252 +      if (info) {
   1.253 +        // This comes from looking at the Darwin xnu Kernel
   1.254 +        if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
   1.255 +          result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
   1.256 +
   1.257 +        free(info);
   1.258 +      }
   1.259 +    }
   1.260 +  }
   1.261 +
   1.262 +  return result;
   1.263 +}
   1.264 +
   1.265 +//=============================================================================
   1.266 +bool Breakpad::ExceptionHandlerDirectCallback(void *context,
   1.267 +                                                    int exception_type,
   1.268 +                                                    int exception_code,
   1.269 +                                                    int exception_subcode,
   1.270 +                                                    mach_port_t crashing_thread) {
   1.271 +  Breakpad *breakpad = (Breakpad *)context;
   1.272 +
   1.273 +  // If our context is damaged or something, just return false to indicate that
   1.274 +  // the handler should continue without us.
   1.275 +  if (!breakpad)
   1.276 +    return false;
   1.277 +
   1.278 +  return breakpad->HandleException( exception_type,
   1.279 +                                    exception_code,
   1.280 +                                    exception_subcode,
   1.281 +                                    crashing_thread);
   1.282 +}
   1.283 +
   1.284 +//=============================================================================
   1.285 +#pragma mark -
   1.286 +
   1.287 +#include <dlfcn.h>
   1.288 +
   1.289 +//=============================================================================
   1.290 +// Returns the pathname to the Resources directory for this version of
   1.291 +// Breakpad which we are now running.
   1.292 +//
   1.293 +// Don't make the function static, since _dyld_lookup_and_bind_fully needs a
   1.294 +// simple non-static C name
   1.295 +//
   1.296 +extern "C" {
   1.297 +NSString * GetResourcePath();
   1.298 +NSString * GetResourcePath() {
   1.299 +  NSString *resourcePath = nil;
   1.300 +
   1.301 +  // If there are multiple breakpads installed then calling bundleWithIdentifier
   1.302 +  // will not work properly, so only use that as a backup plan.
   1.303 +  // We want to find the bundle containing the code where this function lives
   1.304 +  // and work from there
   1.305 +  //
   1.306 +
   1.307 +  // Get the pathname to the code which contains this function
   1.308 +  Dl_info info;
   1.309 +  if (dladdr((const void*)GetResourcePath, &info) != 0) {
   1.310 +    NSFileManager *filemgr = [NSFileManager defaultManager];
   1.311 +    NSString *filePath =
   1.312 +        [filemgr stringWithFileSystemRepresentation:info.dli_fname
   1.313 +                                             length:strlen(info.dli_fname)];
   1.314 +    NSString *bundlePath = [filePath stringByDeletingLastPathComponent];
   1.315 +    // The "Resources" directory should be in the same directory as the
   1.316 +    // executable code, since that's how the Breakpad framework is built.
   1.317 +    resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"];
   1.318 +  } else {
   1.319 +    DEBUGLOG(stderr, "Could not find GetResourcePath\n");
   1.320 +    // fallback plan
   1.321 +    NSBundle *bundle =
   1.322 +        [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
   1.323 +    resourcePath = [bundle resourcePath];
   1.324 +  }
   1.325 +
   1.326 +  return resourcePath;
   1.327 +}
   1.328 +}  // extern "C"
   1.329 +
   1.330 +//=============================================================================
   1.331 +bool Breakpad::Initialize(NSDictionary *parameters) {
   1.332 +  // Initialize
   1.333 +  config_params_ = NULL;
   1.334 +  handler_ = NULL;
   1.335 +
   1.336 +  // Check for debugger
   1.337 +  if (IsDebuggerActive()) {
   1.338 +    DEBUGLOG(stderr, "Debugger is active:  Not installing handler\n");
   1.339 +    return true;
   1.340 +  }
   1.341 +
   1.342 +  // Gather any user specified parameters
   1.343 +  if (!ExtractParameters(parameters)) {
   1.344 +    return false;
   1.345 +  }
   1.346 +
   1.347 +  // Get path to Inspector executable.
   1.348 +  NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION);
   1.349 +
   1.350 +  // Standardize path (resolve symlinkes, etc.)  and escape spaces
   1.351 +  inspectorPathString = [inspectorPathString stringByStandardizingPath];
   1.352 +  inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "]
   1.353 +                                              componentsJoinedByString:@"\\ "];
   1.354 +
   1.355 +  // Create an on-demand server object representing the Inspector.
   1.356 +  // In case of a crash, we simply need to call the LaunchOnDemand()
   1.357 +  // method on it, then send a mach message to its service port.
   1.358 +  // It will then launch and perform a process inspection of our crashed state.
   1.359 +  // See the HandleException() method for the details.
   1.360 +#define RECEIVE_PORT_NAME "com.Breakpad.Inspector"
   1.361 +
   1.362 +  name_t portName;
   1.363 +  snprintf(portName, sizeof(name_t),  "%s%d", RECEIVE_PORT_NAME, getpid());
   1.364 +
   1.365 +  // Save the location of the Inspector
   1.366 +  strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation],
   1.367 +          sizeof(inspector_path_));
   1.368 +
   1.369 +  // Append a single command-line argument to the Inspector path
   1.370 +  // representing the bootstrap name of the launch-on-demand receive port.
   1.371 +  // When the Inspector is launched, it can use this to lookup the port
   1.372 +  // by calling bootstrap_check_in().
   1.373 +  strlcat(inspector_path_, " ", sizeof(inspector_path_));
   1.374 +  strlcat(inspector_path_, portName, sizeof(inspector_path_));
   1.375 +
   1.376 +  kern_return_t kr = inspector_.Initialize(inspector_path_,
   1.377 +                                           portName,
   1.378 +                                           true);        // shutdown on exit
   1.379 +
   1.380 +  if (kr != KERN_SUCCESS) {
   1.381 +    return false;
   1.382 +  }
   1.383 +
   1.384 +  // Create the handler (allocating it in our special protected pool)
   1.385 +  handler_ =
   1.386 +      new (gBreakpadAllocator->Allocate(
   1.387 +          sizeof(google_breakpad::ExceptionHandler)))
   1.388 +          google_breakpad::ExceptionHandler(
   1.389 +              Breakpad::ExceptionHandlerDirectCallback, this, true);
   1.390 +  return true;
   1.391 +}
   1.392 +
   1.393 +//=============================================================================
   1.394 +Breakpad::~Breakpad() {
   1.395 +  // Note that we don't use operator delete() on these pointers,
   1.396 +  // since they were allocated by ProtectedMemoryAllocator objects.
   1.397 +  //
   1.398 +  if (config_params_) {
   1.399 +    config_params_->~SimpleStringDictionary();
   1.400 +  }
   1.401 +
   1.402 +  if (handler_)
   1.403 +    handler_->~ExceptionHandler();
   1.404 +}
   1.405 +
   1.406 +//=============================================================================
   1.407 +bool Breakpad::ExtractParameters(NSDictionary *parameters) {
   1.408 +  NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
   1.409 +  NSString *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM];
   1.410 +  NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT];
   1.411 +
   1.412 +  NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
   1.413 +  NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
   1.414 +  NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
   1.415 +  NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
   1.416 +  NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
   1.417 +  NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
   1.418 +  NSString *inspectorPathString =
   1.419 +      [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
   1.420 +  NSString *reporterPathString =
   1.421 +      [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
   1.422 +  NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
   1.423 +  NSArray  *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
   1.424 +  NSString *logFileTailSize =
   1.425 +      [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
   1.426 +  NSString *requestUserText =
   1.427 +      [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
   1.428 +  NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
   1.429 +  NSString *vendor =
   1.430 +      [parameters objectForKey:@BREAKPAD_VENDOR];
   1.431 +  NSString *dumpSubdirectory =
   1.432 +      [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
   1.433 +
   1.434 +  NSDictionary *serverParameters =
   1.435 +      [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
   1.436 +
   1.437 +  // These may have been set above as user prefs, which take priority.
   1.438 +  if (!skipConfirm) {
   1.439 +    skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
   1.440 +  }
   1.441 +  if (!sendAndExit) {
   1.442 +    sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
   1.443 +  }
   1.444 +
   1.445 +  if (!product)
   1.446 +    product = [parameters objectForKey:@"CFBundleName"];
   1.447 +
   1.448 +  if (!display) {
   1.449 +    display = [parameters objectForKey:@"CFBundleDisplayName"];
   1.450 +    if (!display) {
   1.451 +      display = product;
   1.452 +    }
   1.453 +  }
   1.454 +
   1.455 +  if (!version)
   1.456 +    version = [parameters objectForKey:@"CFBundleVersion"];
   1.457 +
   1.458 +  if (!interval)
   1.459 +    interval = @"3600";
   1.460 +
   1.461 +  if (!timeout)
   1.462 +    timeout = @"300";
   1.463 +
   1.464 +  if (!logFileTailSize)
   1.465 +    logFileTailSize = @"200000";
   1.466 +
   1.467 +  if (!vendor) {
   1.468 +    vendor = @"Vendor not specified";
   1.469 +  }
   1.470 +
   1.471 +  // Normalize the values.
   1.472 +  if (skipConfirm) {
   1.473 +    skipConfirm = [skipConfirm uppercaseString];
   1.474 +
   1.475 +    if ([skipConfirm isEqualToString:@"YES"] ||
   1.476 +        [skipConfirm isEqualToString:@"TRUE"] ||
   1.477 +        [skipConfirm isEqualToString:@"1"])
   1.478 +      skipConfirm = @"YES";
   1.479 +    else
   1.480 +      skipConfirm = @"NO";
   1.481 +  } else {
   1.482 +    skipConfirm = @"NO";
   1.483 +  }
   1.484 +
   1.485 +  send_and_exit_ = true;
   1.486 +  if (sendAndExit) {
   1.487 +    sendAndExit = [sendAndExit uppercaseString];
   1.488 +
   1.489 +    if ([sendAndExit isEqualToString:@"NO"] ||
   1.490 +        [sendAndExit isEqualToString:@"FALSE"] ||
   1.491 +        [sendAndExit isEqualToString:@"0"])
   1.492 +      send_and_exit_ = false;
   1.493 +  }
   1.494 +
   1.495 +  if (requestUserText) {
   1.496 +    requestUserText = [requestUserText uppercaseString];
   1.497 +
   1.498 +    if ([requestUserText isEqualToString:@"YES"] ||
   1.499 +        [requestUserText isEqualToString:@"TRUE"] ||
   1.500 +        [requestUserText isEqualToString:@"1"])
   1.501 +      requestUserText = @"YES";
   1.502 +    else
   1.503 +      requestUserText = @"NO";
   1.504 +  } else {
   1.505 +    requestUserText = @"NO";
   1.506 +  }
   1.507 +
   1.508 +  // Find the helper applications if not specified in user config.
   1.509 +  NSString *resourcePath = nil;
   1.510 +  if (!inspectorPathString || !reporterPathString) {
   1.511 +    resourcePath = GetResourcePath();
   1.512 +    if (!resourcePath) {
   1.513 +      DEBUGLOG(stderr, "Could not get resource path\n");
   1.514 +      return false;
   1.515 +    }
   1.516 +  }
   1.517 +
   1.518 +  // Find Inspector.
   1.519 +  if (!inspectorPathString) {
   1.520 +    inspectorPathString =
   1.521 +        [resourcePath stringByAppendingPathComponent:@"Inspector"];
   1.522 +  }
   1.523 +
   1.524 +  // Verify that there is an Inspector tool.
   1.525 +  if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
   1.526 +    DEBUGLOG(stderr, "Cannot find Inspector tool\n");
   1.527 +    return false;
   1.528 +  }
   1.529 +
   1.530 +  // Find Reporter.
   1.531 +  if (!reporterPathString) {
   1.532 +    reporterPathString =
   1.533 +        [resourcePath
   1.534 +         stringByAppendingPathComponent:@"crash_report_sender.app"];
   1.535 +    reporterPathString =
   1.536 +        [[NSBundle bundleWithPath:reporterPathString] executablePath];
   1.537 +  }
   1.538 +
   1.539 +  // Verify that there is a Reporter application.
   1.540 +  if (![[NSFileManager defaultManager]
   1.541 +             fileExistsAtPath:reporterPathString]) {
   1.542 +    DEBUGLOG(stderr, "Cannot find Reporter tool\n");
   1.543 +    return false;
   1.544 +  }
   1.545 +
   1.546 +  if (!dumpSubdirectory) {
   1.547 +    dumpSubdirectory = @"";
   1.548 +  }
   1.549 +
   1.550 +  // The product, version, and URL are required values.
   1.551 +  if (![product length]) {
   1.552 +    DEBUGLOG(stderr, "Missing required product key.\n");
   1.553 +    return false;
   1.554 +  }
   1.555 +
   1.556 +  if (![version length]) {
   1.557 +    DEBUGLOG(stderr, "Missing required version key.\n");
   1.558 +    return false;
   1.559 +  }
   1.560 +
   1.561 +  if (![urlStr length]) {
   1.562 +    DEBUGLOG(stderr, "Missing required URL key.\n");
   1.563 +    return false;
   1.564 +  }
   1.565 +
   1.566 +  config_params_ =
   1.567 +      new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
   1.568 +        SimpleStringDictionary();
   1.569 +
   1.570 +  SimpleStringDictionary &dictionary = *config_params_;
   1.571 +
   1.572 +  dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE,     [serverType UTF8String]);
   1.573 +  dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
   1.574 +  dictionary.SetKeyValue(BREAKPAD_PRODUCT,         [product UTF8String]);
   1.575 +  dictionary.SetKeyValue(BREAKPAD_VERSION,         [version UTF8String]);
   1.576 +  dictionary.SetKeyValue(BREAKPAD_URL,             [urlStr UTF8String]);
   1.577 +  dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]);
   1.578 +  dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM,    [skipConfirm UTF8String]);
   1.579 +  dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]);
   1.580 +  dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION,
   1.581 +                         [inspectorPathString fileSystemRepresentation]);
   1.582 +  dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION,
   1.583 +                         [reporterPathString fileSystemRepresentation]);
   1.584 +  dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE,
   1.585 +                         [logFileTailSize UTF8String]);
   1.586 +  dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
   1.587 +                         [requestUserText UTF8String]);
   1.588 +  dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]);
   1.589 +  dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
   1.590 +  dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
   1.591 +                         [dumpSubdirectory UTF8String]);
   1.592 +
   1.593 +  struct timeval tv;
   1.594 +  gettimeofday(&tv, NULL);
   1.595 +  char timeStartedString[32];
   1.596 +  sprintf(timeStartedString, "%zd", tv.tv_sec);
   1.597 +  dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME,
   1.598 +                         timeStartedString);
   1.599 +
   1.600 +  if (logFilePaths) {
   1.601 +    char logFileKey[255];
   1.602 +    for(unsigned int i = 0; i < [logFilePaths count]; i++) {
   1.603 +      sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i);
   1.604 +      dictionary.SetKeyValue(logFileKey,
   1.605 +                             [[logFilePaths objectAtIndex:i]
   1.606 +                               fileSystemRepresentation]);
   1.607 +    }
   1.608 +  }
   1.609 +
   1.610 +  if (serverParameters) {
   1.611 +    // For each key-value pair, call BreakpadAddUploadParameter()
   1.612 +    NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
   1.613 +    NSString *aParameter;
   1.614 +    while ((aParameter = [keyEnumerator nextObject])) {
   1.615 +      BreakpadAddUploadParameter(this, aParameter,
   1.616 +				 [serverParameters objectForKey:aParameter]);
   1.617 +    }
   1.618 +  }
   1.619 +  return true;
   1.620 +}
   1.621 +
   1.622 +//=============================================================================
   1.623 +void Breakpad::SetKeyValue(NSString *key, NSString *value) {
   1.624 +  // We allow nil values. This is the same as removing the keyvalue.
   1.625 +  if (!config_params_ || !key)
   1.626 +    return;
   1.627 +
   1.628 +  config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
   1.629 +}
   1.630 +
   1.631 +//=============================================================================
   1.632 +NSString *Breakpad::KeyValue(NSString *key) {
   1.633 +  if (!config_params_ || !key)
   1.634 +    return nil;
   1.635 +
   1.636 +  const char *value = config_params_->GetValueForKey([key UTF8String]);
   1.637 +  return value ? [NSString stringWithUTF8String:value] : nil;
   1.638 +}
   1.639 +
   1.640 +//=============================================================================
   1.641 +void Breakpad::RemoveKeyValue(NSString *key) {
   1.642 +  if (!config_params_ || !key) return;
   1.643 +
   1.644 +  config_params_->RemoveKey([key UTF8String]);
   1.645 +}
   1.646 +
   1.647 +//=============================================================================
   1.648 +void Breakpad::GenerateAndSendReport() {
   1.649 +  config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES");
   1.650 +  HandleException(0, 0, 0, mach_thread_self());
   1.651 +  config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO");
   1.652 +}
   1.653 +
   1.654 +//=============================================================================
   1.655 +bool Breakpad::HandleException(int exception_type,
   1.656 +                               int exception_code,
   1.657 +                               int exception_subcode,
   1.658 +                               mach_port_t crashing_thread) {
   1.659 +  DEBUGLOG(stderr, "Breakpad: an exception occurred\n");
   1.660 +
   1.661 +  if (filter_callback_) {
   1.662 +    bool should_handle = filter_callback_(exception_type,
   1.663 +                                          exception_code,
   1.664 +                                          crashing_thread,
   1.665 +                                          filter_callback_context_);
   1.666 +    if (!should_handle) return false;
   1.667 +  }
   1.668 +
   1.669 +  // We need to reset the memory protections to be read/write,
   1.670 +  // since LaunchOnDemand() requires changing state.
   1.671 +  gBreakpadAllocator->Unprotect();
   1.672 +  // Configure the server to launch when we message the service port.
   1.673 +  // The reason we do this here, rather than at startup, is that we
   1.674 +  // can leak a bootstrap service entry if this method is called and
   1.675 +  // there never ends up being a crash.
   1.676 +  inspector_.LaunchOnDemand();
   1.677 +  gBreakpadAllocator->Protect();
   1.678 +
   1.679 +  // The Inspector should send a message to this port to verify it
   1.680 +  // received our information and has finished the inspection.
   1.681 +  ReceivePort acknowledge_port;
   1.682 +
   1.683 +  // Send initial information to the Inspector.
   1.684 +  MachSendMessage message(kMsgType_InspectorInitialInfo);
   1.685 +  message.AddDescriptor(mach_task_self());          // our task
   1.686 +  message.AddDescriptor(crashing_thread);           // crashing thread
   1.687 +  message.AddDescriptor(mach_thread_self());        // exception-handling thread
   1.688 +  message.AddDescriptor(acknowledge_port.GetPort());// message receive port
   1.689 +
   1.690 +  InspectorInfo info;
   1.691 +  info.exception_type = exception_type;
   1.692 +  info.exception_code = exception_code;
   1.693 +  info.exception_subcode = exception_subcode;
   1.694 +  info.parameter_count = config_params_->GetCount();
   1.695 +  message.SetData(&info, sizeof(info));
   1.696 +
   1.697 +  MachPortSender sender(inspector_.GetServicePort());
   1.698 +
   1.699 +  kern_return_t result = sender.SendMessage(message, 2000);
   1.700 +
   1.701 +  if (result == KERN_SUCCESS) {
   1.702 +    // Now, send a series of key-value pairs to the Inspector.
   1.703 +    const KeyValueEntry *entry = NULL;
   1.704 +    SimpleStringDictionaryIterator iter(*config_params_);
   1.705 +
   1.706 +    while ( (entry = iter.Next()) ) {
   1.707 +      KeyValueMessageData keyvalue_data(*entry);
   1.708 +
   1.709 +      MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair);
   1.710 +      keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data));
   1.711 +
   1.712 +      result = sender.SendMessage(keyvalue_message, 2000);
   1.713 +
   1.714 +      if (result != KERN_SUCCESS) {
   1.715 +        break;
   1.716 +      }
   1.717 +    }
   1.718 +
   1.719 +    if (result == KERN_SUCCESS) {
   1.720 +      // Wait for acknowledgement that the inspection has finished.
   1.721 +      MachReceiveMessage acknowledge_messsage;
   1.722 +      result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000);
   1.723 +    }
   1.724 +  }
   1.725 +
   1.726 +#if VERBOSE
   1.727 +  PRINT_MACH_RESULT(result, "Breakpad: SendMessage ");
   1.728 +  printf("Breakpad: Inspector service port = %#x\n",
   1.729 +    inspector_.GetServicePort());
   1.730 +#endif
   1.731 +
   1.732 +  // If we don't want any forwarding, return true here to indicate that we've
   1.733 +  // processed things as much as we want.
   1.734 +  if (send_and_exit_) return true;
   1.735 +
   1.736 +  return false;
   1.737 +}
   1.738 +
   1.739 +//=============================================================================
   1.740 +//=============================================================================
   1.741 +
   1.742 +#pragma mark -
   1.743 +#pragma mark Public API
   1.744 +
   1.745 +//=============================================================================
   1.746 +BreakpadRef BreakpadCreate(NSDictionary *parameters) {
   1.747 +  try {
   1.748 +    // This is confusing.  Our two main allocators for breakpad memory are:
   1.749 +    //    - gKeyValueAllocator for the key/value memory
   1.750 +    //    - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
   1.751 +    //      breakpad allocations which are accessed at exception handling time.
   1.752 +    //
   1.753 +    // But in order to avoid these two allocators themselves from being smashed,
   1.754 +    // we'll protect them as well by allocating them with gMasterAllocator.
   1.755 +    //
   1.756 +    // gMasterAllocator itself will NOT be protected, but this doesn't matter,
   1.757 +    // since once it does its allocations and locks the memory, smashes to itself
   1.758 +    // don't affect anything we care about.
   1.759 +    gMasterAllocator =
   1.760 +        new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
   1.761 +
   1.762 +    gKeyValueAllocator =
   1.763 +        new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
   1.764 +            ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
   1.765 +
   1.766 +    // Create a mutex for use in accessing the SimpleStringDictionary
   1.767 +    int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
   1.768 +    if (mutexResult == 0) {
   1.769 +
   1.770 +      // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
   1.771 +      // Let's round up to the nearest page size.
   1.772 +      //
   1.773 +      int breakpad_pool_size = 4096;
   1.774 +
   1.775 +      /*
   1.776 +       sizeof(Breakpad)
   1.777 +       + sizeof(google_breakpad::ExceptionHandler)
   1.778 +       + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
   1.779 +       */
   1.780 +
   1.781 +      gBreakpadAllocator =
   1.782 +          new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
   1.783 +              ProtectedMemoryAllocator(breakpad_pool_size);
   1.784 +
   1.785 +      // Stack-based autorelease pool for Breakpad::Create() obj-c code.
   1.786 +      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   1.787 +      Breakpad *breakpad = Breakpad::Create(parameters);
   1.788 +
   1.789 +      if (breakpad) {
   1.790 +        // Make read-only to protect against memory smashers
   1.791 +        gMasterAllocator->Protect();
   1.792 +        gKeyValueAllocator->Protect();
   1.793 +        gBreakpadAllocator->Protect();
   1.794 +        // Can uncomment this line to figure out how much space was actually
   1.795 +        // allocated using this allocator
   1.796 +        //     printf("gBreakpadAllocator allocated size = %d\n",
   1.797 +        //         gBreakpadAllocator->GetAllocatedSize() );
   1.798 +        [pool release];
   1.799 +        return (BreakpadRef)breakpad;
   1.800 +      }
   1.801 +
   1.802 +      [pool release];
   1.803 +    }
   1.804 +  } catch(...) {    // don't let exceptions leave this C API
   1.805 +    fprintf(stderr, "BreakpadCreate() : error\n");
   1.806 +  }
   1.807 +
   1.808 +  if (gKeyValueAllocator) {
   1.809 +    gKeyValueAllocator->~ProtectedMemoryAllocator();
   1.810 +    gKeyValueAllocator = NULL;
   1.811 +  }
   1.812 +
   1.813 +  if (gBreakpadAllocator) {
   1.814 +    gBreakpadAllocator->~ProtectedMemoryAllocator();
   1.815 +    gBreakpadAllocator = NULL;
   1.816 +  }
   1.817 +
   1.818 +  delete gMasterAllocator;
   1.819 +  gMasterAllocator = NULL;
   1.820 +
   1.821 +  return NULL;
   1.822 +}
   1.823 +
   1.824 +//=============================================================================
   1.825 +void BreakpadRelease(BreakpadRef ref) {
   1.826 +  try {
   1.827 +    Breakpad *breakpad = (Breakpad *)ref;
   1.828 +
   1.829 +    if (gMasterAllocator) {
   1.830 +      gMasterAllocator->Unprotect();
   1.831 +      gKeyValueAllocator->Unprotect();
   1.832 +      gBreakpadAllocator->Unprotect();
   1.833 +
   1.834 +      breakpad->~Breakpad();
   1.835 +
   1.836 +      // Unfortunately, it's not possible to deallocate this stuff
   1.837 +      // because the exception handling thread is still finishing up
   1.838 +      // asynchronously at this point...  OK, it could be done with
   1.839 +      // locks, etc.  But since BreakpadRelease() should usually only
   1.840 +      // be called right before the process exits, it's not worth
   1.841 +      // deallocating this stuff.
   1.842 +#if 0
   1.843 +      gKeyValueAllocator->~ProtectedMemoryAllocator();
   1.844 +      gBreakpadAllocator->~ProtectedMemoryAllocator();
   1.845 +      delete gMasterAllocator;
   1.846 +
   1.847 +      gMasterAllocator = NULL;
   1.848 +      gKeyValueAllocator = NULL;
   1.849 +      gBreakpadAllocator = NULL;
   1.850 +#endif
   1.851 +
   1.852 +      pthread_mutex_destroy(&gDictionaryMutex);
   1.853 +    }
   1.854 +  } catch(...) {    // don't let exceptions leave this C API
   1.855 +    fprintf(stderr, "BreakpadRelease() : error\n");
   1.856 +  }
   1.857 +}
   1.858 +
   1.859 +//=============================================================================
   1.860 +void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
   1.861 +  try {
   1.862 +    // Not called at exception time
   1.863 +    Breakpad *breakpad = (Breakpad *)ref;
   1.864 +
   1.865 +    if (breakpad && key && gKeyValueAllocator) {
   1.866 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.867 +
   1.868 +      breakpad->SetKeyValue(key, value);
   1.869 +    }
   1.870 +  } catch(...) {    // don't let exceptions leave this C API
   1.871 +    fprintf(stderr, "BreakpadSetKeyValue() : error\n");
   1.872 +  }
   1.873 +}
   1.874 +
   1.875 +void BreakpadAddUploadParameter(BreakpadRef ref,
   1.876 +                                NSString *key,
   1.877 +                                NSString *value) {
   1.878 +  // The only difference, internally, between an upload parameter and
   1.879 +  // a key value one that is set with BreakpadSetKeyValue is that we
   1.880 +  // prepend the keyname with a special prefix.  This informs the
   1.881 +  // crash sender that the parameter should be sent along with the
   1.882 +  // POST of the crash dump upload.
   1.883 +  try {
   1.884 +    Breakpad *breakpad = (Breakpad *)ref;
   1.885 +
   1.886 +    if (breakpad && key && gKeyValueAllocator) {
   1.887 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.888 +
   1.889 +      NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
   1.890 +				stringByAppendingString:key];
   1.891 +      breakpad->SetKeyValue(prefixedKey, value);
   1.892 +    }
   1.893 +  } catch(...) {    // don't let exceptions leave this C API
   1.894 +    fprintf(stderr, "BreakpadSetKeyValue() : error\n");
   1.895 +  }
   1.896 +}
   1.897 +
   1.898 +void BreakpadRemoveUploadParameter(BreakpadRef ref,
   1.899 +                                   NSString *key) {
   1.900 +  try {
   1.901 +    // Not called at exception time
   1.902 +    Breakpad *breakpad = (Breakpad *)ref;
   1.903 +
   1.904 +    if (breakpad && key && gKeyValueAllocator) {
   1.905 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.906 +
   1.907 +      NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
   1.908 +                                        @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
   1.909 +      breakpad->RemoveKeyValue(prefixedKey);
   1.910 +    }
   1.911 +  } catch(...) {    // don't let exceptions leave this C API
   1.912 +    fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
   1.913 +  }
   1.914 +}
   1.915 +//=============================================================================
   1.916 +NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
   1.917 +  NSString *value = nil;
   1.918 +
   1.919 +  try {
   1.920 +    // Not called at exception time
   1.921 +    Breakpad *breakpad = (Breakpad *)ref;
   1.922 +
   1.923 +    if (!breakpad || !key || !gKeyValueAllocator)
   1.924 +      return nil;
   1.925 +
   1.926 +    ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.927 +
   1.928 +    value = breakpad->KeyValue(key);
   1.929 +  } catch(...) {    // don't let exceptions leave this C API
   1.930 +    fprintf(stderr, "BreakpadKeyValue() : error\n");
   1.931 +  }
   1.932 +
   1.933 +  return value;
   1.934 +}
   1.935 +
   1.936 +//=============================================================================
   1.937 +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
   1.938 +  try {
   1.939 +    // Not called at exception time
   1.940 +    Breakpad *breakpad = (Breakpad *)ref;
   1.941 +
   1.942 +    if (breakpad && key && gKeyValueAllocator) {
   1.943 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.944 +
   1.945 +      breakpad->RemoveKeyValue(key);
   1.946 +    }
   1.947 +  } catch(...) {    // don't let exceptions leave this C API
   1.948 +    fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
   1.949 +  }
   1.950 +}
   1.951 +
   1.952 +//=============================================================================
   1.953 +void BreakpadGenerateAndSendReport(BreakpadRef ref) {
   1.954 +  try {
   1.955 +    Breakpad *breakpad = (Breakpad *)ref;
   1.956 +
   1.957 +    if (breakpad && gKeyValueAllocator) {
   1.958 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.959 +
   1.960 +      gBreakpadAllocator->Unprotect();
   1.961 +      breakpad->GenerateAndSendReport();
   1.962 +      gBreakpadAllocator->Protect();
   1.963 +    }
   1.964 +  } catch(...) {    // don't let exceptions leave this C API
   1.965 +    fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
   1.966 +  }
   1.967 +}
   1.968 +
   1.969 +//=============================================================================
   1.970 +void BreakpadSetFilterCallback(BreakpadRef ref,
   1.971 +                               BreakpadFilterCallback callback,
   1.972 +                               void *context) {
   1.973 +
   1.974 +  try {
   1.975 +    Breakpad *breakpad = (Breakpad *)ref;
   1.976 +
   1.977 +    if (breakpad && gBreakpadAllocator) {
   1.978 +      // share the dictionary mutex here (we really don't need a mutex)
   1.979 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator);
   1.980 +
   1.981 +      breakpad->SetFilterCallback(callback, context);
   1.982 +    }
   1.983 +  } catch(...) {    // don't let exceptions leave this C API
   1.984 +    fprintf(stderr, "BreakpadSetFilterCallback() : error\n");
   1.985 +  }
   1.986 +}
   1.987 +
   1.988 +//============================================================================
   1.989 +void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) {
   1.990 +  int logFileCounter = 0;
   1.991 +
   1.992 +  NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
   1.993 +                                   @BREAKPAD_LOGFILE_KEY_PREFIX,
   1.994 +                                   logFileCounter];
   1.995 +
   1.996 +  NSString *existingLogFilename = nil;
   1.997 +  existingLogFilename = BreakpadKeyValue(ref, logFileKey);
   1.998 +  // Find the first log file key that we can use by testing for existence
   1.999 +  while (existingLogFilename) {
  1.1000 +    if ([existingLogFilename isEqualToString:logPathname]) {
  1.1001 +      return;
  1.1002 +    }
  1.1003 +    logFileCounter++;
  1.1004 +    logFileKey = [NSString stringWithFormat:@"%@%d",
  1.1005 +                           @BREAKPAD_LOGFILE_KEY_PREFIX,
  1.1006 +                           logFileCounter];
  1.1007 +    existingLogFilename = BreakpadKeyValue(ref, logFileKey);
  1.1008 +  }
  1.1009 +
  1.1010 +  BreakpadSetKeyValue(ref, logFileKey, logPathname);
  1.1011 +}

mercurial