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

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial