1 // Copyright (c) 2006, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
34 #import "client/mac/Framework/Breakpad.h"
37 #import <Foundation/Foundation.h>
40 #include <sys/sysctl.h>
42 #import "client/mac/crash_generation/Inspector.h"
43 #import "client/mac/handler/exception_handler.h"
44 #import "client/mac/Framework/Breakpad.h"
45 #import "client/mac/Framework/OnDemandServer.h"
46 #import "client/mac/handler/protected_memory_allocator.h"
47 #import "common/mac/MachIPC.h"
48 #import "common/simple_string_dictionary.h"
51 // This file uses C++ try/catch (but shouldn't). Duplicate the macros from
52 // <c++/4.2.1/exception_defines.h> allowing this file to work properly with
53 // exceptions disabled even when other C++ libraries are used. #undef the try
54 // and catch macros first in case libstdc++ is in use and has already provided
55 // its own definitions.
59 #define catch(X) if (false)
60 #endif // __EXCEPTIONS
62 using google_breakpad::MachPortSender;
63 using google_breakpad::MachReceiveMessage;
64 using google_breakpad::MachSendMessage;
65 using google_breakpad::ReceivePort;
66 using google_breakpad::SimpleStringDictionary;
68 //=============================================================================
69 // We want any memory allocations which are used by breakpad during the
70 // exception handling process (after a crash has happened) to be read-only
71 // to prevent them from being smashed before a crash occurs. Unfortunately
72 // we cannot protect against smashes to our exception handling thread's
75 // NOTE: Any memory allocations which are not used during the exception
76 // handling process may be allocated in the normal ways.
78 // The ProtectedMemoryAllocator class provides an Allocate() method which
79 // we'll using in conjunction with placement operator new() to control
80 // allocation of C++ objects. Note that we don't use operator delete()
81 // but instead call the objects destructor directly: object->~ClassName();
83 ProtectedMemoryAllocator *gMasterAllocator = NULL;
84 ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
85 ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
87 // Mutex for thread-safe access to the key/value dictionary used by breakpad.
88 // It's a global instead of an instance variable of Breakpad
89 // since it can't live in a protected memory area.
90 pthread_mutex_t gDictionaryMutex;
92 //=============================================================================
93 // Stack-based object for thread-safe access to a memory-protected region.
94 // It's assumed that normally the memory block (allocated by the allocator)
95 // is protected (read-only). Creating a stack-based instance of
96 // ProtectedMemoryLocker will unprotect this block after taking the lock.
97 // Its destructor will first re-protect the memory then release the lock.
98 class ProtectedMemoryLocker {
100 ProtectedMemoryLocker(pthread_mutex_t *mutex,
101 ProtectedMemoryAllocator *allocator)
103 allocator_(allocator) {
105 __attribute__((unused)) int rv = pthread_mutex_lock(mutex_);
108 // Unprotect the memory
109 allocator_->Unprotect();
112 ~ProtectedMemoryLocker() {
113 // First protect the memory
114 allocator_->Protect();
116 // Then unlock the mutex
117 __attribute__((unused)) int rv = pthread_mutex_unlock(mutex_);
122 ProtectedMemoryLocker();
123 ProtectedMemoryLocker(const ProtectedMemoryLocker&);
124 ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&);
126 pthread_mutex_t *mutex_;
127 ProtectedMemoryAllocator *allocator_;
130 //=============================================================================
134 static Breakpad *Create(NSDictionary *parameters) {
135 // Allocate from our special allocation pool
137 new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
143 if (!breakpad->Initialize(parameters)) {
144 // Don't use operator delete() here since we allocated from special pool
145 breakpad->~Breakpad();
154 void SetKeyValue(NSString *key, NSString *value);
155 NSString *KeyValue(NSString *key);
156 void RemoveKeyValue(NSString *key);
158 void GenerateAndSendReport();
160 void SetFilterCallback(BreakpadFilterCallback callback, void *context) {
161 filter_callback_ = callback;
162 filter_callback_context_ = context;
168 config_params_(NULL),
169 send_and_exit_(true),
170 filter_callback_(NULL),
171 filter_callback_context_(NULL) {
172 inspector_path_[0] = 0;
175 bool Initialize(NSDictionary *parameters);
177 bool ExtractParameters(NSDictionary *parameters);
179 // Dispatches to HandleException()
180 static bool ExceptionHandlerDirectCallback(void *context,
183 int exception_subcode,
184 mach_port_t crashing_thread);
186 bool HandleException(int exception_type,
188 int exception_subcode,
189 mach_port_t crashing_thread);
191 // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
192 // MachineExceptions.h, we have to explicitly name the handler.
193 google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
195 char inspector_path_[PATH_MAX]; // Path to inspector tool
197 SimpleStringDictionary *config_params_; // Create parameters (STRONG)
199 OnDemandServer inspector_;
201 bool send_and_exit_; // Exit after sending, if true
203 BreakpadFilterCallback filter_callback_;
204 void *filter_callback_context_;
208 #pragma mark Helper functions
210 //=============================================================================
213 //=============================================================================
214 static BOOL IsDebuggerActive() {
216 NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
218 // We check both defaults and the environment variable here
220 BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
222 if (!ignoreDebugger) {
223 char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
224 ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
227 if (!ignoreDebugger) {
228 pid_t pid = getpid();
229 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
230 int mibSize = sizeof(mib) / sizeof(int);
233 if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
234 struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
237 // This comes from looking at the Darwin xnu Kernel
238 if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
239 result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
249 //=============================================================================
250 bool Breakpad::ExceptionHandlerDirectCallback(void *context,
253 int exception_subcode,
254 mach_port_t crashing_thread) {
255 Breakpad *breakpad = (Breakpad *)context;
257 // If our context is damaged or something, just return false to indicate that
258 // the handler should continue without us.
262 return breakpad->HandleException( exception_type,
268 //=============================================================================
273 //=============================================================================
274 // Returns the pathname to the Resources directory for this version of
275 // Breakpad which we are now running.
277 // Don't make the function static, since _dyld_lookup_and_bind_fully needs a
278 // simple non-static C name
281 NSString * GetResourcePath();
282 NSString * GetResourcePath() {
283 NSString *resourcePath = nil;
285 // If there are multiple breakpads installed then calling bundleWithIdentifier
286 // will not work properly, so only use that as a backup plan.
287 // We want to find the bundle containing the code where this function lives
288 // and work from there
291 // Get the pathname to the code which contains this function
293 if (dladdr((const void*)GetResourcePath, &info) != 0) {
294 NSFileManager *filemgr = [NSFileManager defaultManager];
296 [filemgr stringWithFileSystemRepresentation:info.dli_fname
297 length:strlen(info.dli_fname)];
298 NSString *bundlePath = [filePath stringByDeletingLastPathComponent];
299 // The "Resources" directory should be in the same directory as the
300 // executable code, since that's how the Breakpad framework is built.
301 resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"];
305 [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
306 resourcePath = [bundle resourcePath];
313 //=============================================================================
314 bool Breakpad::Initialize(NSDictionary *parameters) {
316 config_params_ = NULL;
319 // Check for debugger
320 if (IsDebuggerActive()) {
324 // Gather any user specified parameters
325 if (!ExtractParameters(parameters)) {
329 // Get path to Inspector executable.
330 NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION);
332 // Standardize path (resolve symlinkes, etc.) and escape spaces
333 inspectorPathString = [inspectorPathString stringByStandardizingPath];
334 inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "]
335 componentsJoinedByString:@"\\ "];
337 // Create an on-demand server object representing the Inspector.
338 // In case of a crash, we simply need to call the LaunchOnDemand()
339 // method on it, then send a mach message to its service port.
340 // It will then launch and perform a process inspection of our crashed state.
341 // See the HandleException() method for the details.
342 #define RECEIVE_PORT_NAME "com.Breakpad.Inspector"
345 snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid());
347 // Save the location of the Inspector
348 strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation],
349 sizeof(inspector_path_));
351 // Append a single command-line argument to the Inspector path
352 // representing the bootstrap name of the launch-on-demand receive port.
353 // When the Inspector is launched, it can use this to lookup the port
354 // by calling bootstrap_check_in().
355 strlcat(inspector_path_, " ", sizeof(inspector_path_));
356 strlcat(inspector_path_, portName, sizeof(inspector_path_));
358 kern_return_t kr = inspector_.Initialize(inspector_path_,
360 true); // shutdown on exit
362 if (kr != KERN_SUCCESS) {
366 // Create the handler (allocating it in our special protected pool)
368 new (gBreakpadAllocator->Allocate(
369 sizeof(google_breakpad::ExceptionHandler)))
370 google_breakpad::ExceptionHandler(
371 Breakpad::ExceptionHandlerDirectCallback, this, true);
375 //=============================================================================
376 Breakpad::~Breakpad() {
377 // Note that we don't use operator delete() on these pointers,
378 // since they were allocated by ProtectedMemoryAllocator objects.
380 if (config_params_) {
381 config_params_->~SimpleStringDictionary();
385 handler_->~ExceptionHandler();
388 //=============================================================================
389 bool Breakpad::ExtractParameters(NSDictionary *parameters) {
390 NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
391 NSString *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM];
392 NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT];
394 NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
395 NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
396 NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
397 NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
398 NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
399 NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
400 NSString *inspectorPathString =
401 [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
402 NSString *reporterPathString =
403 [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
404 NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
405 NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
406 NSString *logFileTailSize =
407 [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
408 NSString *requestUserText =
409 [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
410 NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
412 [parameters objectForKey:@BREAKPAD_VENDOR];
413 NSString *dumpSubdirectory =
414 [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
416 NSDictionary *serverParameters =
417 [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
419 // These may have been set above as user prefs, which take priority.
421 skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
424 sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
428 product = [parameters objectForKey:@"CFBundleName"];
431 display = [parameters objectForKey:@"CFBundleDisplayName"];
438 version = [parameters objectForKey:@"CFBundleVersion"];
446 if (!logFileTailSize)
447 logFileTailSize = @"200000";
450 vendor = @"Vendor not specified";
453 // Normalize the values.
455 skipConfirm = [skipConfirm uppercaseString];
457 if ([skipConfirm isEqualToString:@"YES"] ||
458 [skipConfirm isEqualToString:@"TRUE"] ||
459 [skipConfirm isEqualToString:@"1"])
460 skipConfirm = @"YES";
467 send_and_exit_ = true;
469 sendAndExit = [sendAndExit uppercaseString];
471 if ([sendAndExit isEqualToString:@"NO"] ||
472 [sendAndExit isEqualToString:@"FALSE"] ||
473 [sendAndExit isEqualToString:@"0"])
474 send_and_exit_ = false;
477 if (requestUserText) {
478 requestUserText = [requestUserText uppercaseString];
480 if ([requestUserText isEqualToString:@"YES"] ||
481 [requestUserText isEqualToString:@"TRUE"] ||
482 [requestUserText isEqualToString:@"1"])
483 requestUserText = @"YES";
485 requestUserText = @"NO";
487 requestUserText = @"NO";
490 // Find the helper applications if not specified in user config.
491 NSString *resourcePath = nil;
492 if (!inspectorPathString || !reporterPathString) {
493 resourcePath = GetResourcePath();
500 if (!inspectorPathString) {
501 inspectorPathString =
502 [resourcePath stringByAppendingPathComponent:@"Inspector"];
505 // Verify that there is an Inspector tool.
506 if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
511 if (!reporterPathString) {
514 stringByAppendingPathComponent:@"crash_report_sender.app"];
516 [[NSBundle bundleWithPath:reporterPathString] executablePath];
519 // Verify that there is a Reporter application.
520 if (![[NSFileManager defaultManager]
521 fileExistsAtPath:reporterPathString]) {
525 if (!dumpSubdirectory) {
526 dumpSubdirectory = @"";
529 // The product, version, and URL are required values.
530 if (![product length]) {
534 if (![version length]) {
538 if (![urlStr length]) {
543 new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
544 SimpleStringDictionary();
546 SimpleStringDictionary &dictionary = *config_params_;
548 dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]);
549 dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
550 dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]);
551 dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]);
552 dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]);
553 dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]);
554 dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]);
555 dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]);
556 dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION,
557 [inspectorPathString fileSystemRepresentation]);
558 dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION,
559 [reporterPathString fileSystemRepresentation]);
560 dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE,
561 [logFileTailSize UTF8String]);
562 dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
563 [requestUserText UTF8String]);
564 dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]);
565 dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
566 dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
567 [dumpSubdirectory UTF8String]);
570 gettimeofday(&tv, NULL);
571 char timeStartedString[32];
572 sprintf(timeStartedString, "%zd", tv.tv_sec);
573 dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME,
577 char logFileKey[255];
578 for(unsigned int i = 0; i < [logFilePaths count]; i++) {
579 sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i);
580 dictionary.SetKeyValue(logFileKey,
581 [[logFilePaths objectAtIndex:i]
582 fileSystemRepresentation]);
586 if (serverParameters) {
587 // For each key-value pair, call BreakpadAddUploadParameter()
588 NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
589 NSString *aParameter;
590 while ((aParameter = [keyEnumerator nextObject])) {
591 BreakpadAddUploadParameter(this, aParameter,
592 [serverParameters objectForKey:aParameter]);
598 //=============================================================================
599 void Breakpad::SetKeyValue(NSString *key, NSString *value) {
600 // We allow nil values. This is the same as removing the keyvalue.
601 if (!config_params_ || !key)
604 config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
607 //=============================================================================
608 NSString *Breakpad::KeyValue(NSString *key) {
609 if (!config_params_ || !key)
612 const char *value = config_params_->GetValueForKey([key UTF8String]);
613 return value ? [NSString stringWithUTF8String:value] : nil;
616 //=============================================================================
617 void Breakpad::RemoveKeyValue(NSString *key) {
618 if (!config_params_ || !key) return;
620 config_params_->RemoveKey([key UTF8String]);
623 //=============================================================================
624 void Breakpad::GenerateAndSendReport() {
625 config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES");
626 HandleException(0, 0, 0, mach_thread_self());
627 config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO");
630 //=============================================================================
631 bool Breakpad::HandleException(int exception_type,
633 int exception_subcode,
634 mach_port_t crashing_thread) {
635 if (filter_callback_) {
636 bool should_handle = filter_callback_(exception_type,
639 filter_callback_context_);
640 if (!should_handle) return false;
643 // We need to reset the memory protections to be read/write,
644 // since LaunchOnDemand() requires changing state.
645 gBreakpadAllocator->Unprotect();
646 // Configure the server to launch when we message the service port.
647 // The reason we do this here, rather than at startup, is that we
648 // can leak a bootstrap service entry if this method is called and
649 // there never ends up being a crash.
650 inspector_.LaunchOnDemand();
651 gBreakpadAllocator->Protect();
653 // The Inspector should send a message to this port to verify it
654 // received our information and has finished the inspection.
655 ReceivePort acknowledge_port;
657 // Send initial information to the Inspector.
658 MachSendMessage message(kMsgType_InspectorInitialInfo);
659 message.AddDescriptor(mach_task_self()); // our task
660 message.AddDescriptor(crashing_thread); // crashing thread
661 message.AddDescriptor(mach_thread_self()); // exception-handling thread
662 message.AddDescriptor(acknowledge_port.GetPort());// message receive port
665 info.exception_type = exception_type;
666 info.exception_code = exception_code;
667 info.exception_subcode = exception_subcode;
668 info.parameter_count = config_params_->GetCount();
669 message.SetData(&info, sizeof(info));
671 MachPortSender sender(inspector_.GetServicePort());
673 kern_return_t result = sender.SendMessage(message, 2000);
675 if (result == KERN_SUCCESS) {
676 // Now, send a series of key-value pairs to the Inspector.
677 const SimpleStringDictionary::Entry *entry = NULL;
678 SimpleStringDictionary::Iterator iter(*config_params_);
680 while ( (entry = iter.Next()) ) {
681 KeyValueMessageData keyvalue_data(*entry);
683 MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair);
684 keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data));
686 result = sender.SendMessage(keyvalue_message, 2000);
688 if (result != KERN_SUCCESS) {
693 if (result == KERN_SUCCESS) {
694 // Wait for acknowledgement that the inspection has finished.
695 MachReceiveMessage acknowledge_messsage;
696 result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000);
701 PRINT_MACH_RESULT(result, "Breakpad: SendMessage ");
702 printf("Breakpad: Inspector service port = %#x\n",
703 inspector_.GetServicePort());
706 // If we don't want any forwarding, return true here to indicate that we've
707 // processed things as much as we want.
708 if (send_and_exit_) return true;
713 //=============================================================================
714 //=============================================================================
717 #pragma mark Public API
719 //=============================================================================
720 BreakpadRef BreakpadCreate(NSDictionary *parameters) {
722 // This is confusing. Our two main allocators for breakpad memory are:
723 // - gKeyValueAllocator for the key/value memory
724 // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
725 // breakpad allocations which are accessed at exception handling time.
727 // But in order to avoid these two allocators themselves from being smashed,
728 // we'll protect them as well by allocating them with gMasterAllocator.
730 // gMasterAllocator itself will NOT be protected, but this doesn't matter,
731 // since once it does its allocations and locks the memory, smashes to itself
732 // don't affect anything we care about.
734 new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
737 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
738 ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
740 // Create a mutex for use in accessing the SimpleStringDictionary
741 int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
742 if (mutexResult == 0) {
744 // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
745 // Let's round up to the nearest page size.
747 int breakpad_pool_size = 4096;
751 + sizeof(google_breakpad::ExceptionHandler)
752 + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
756 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
757 ProtectedMemoryAllocator(breakpad_pool_size);
759 // Stack-based autorelease pool for Breakpad::Create() obj-c code.
760 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
761 Breakpad *breakpad = Breakpad::Create(parameters);
764 // Make read-only to protect against memory smashers
765 gMasterAllocator->Protect();
766 gKeyValueAllocator->Protect();
767 gBreakpadAllocator->Protect();
768 // Can uncomment this line to figure out how much space was actually
769 // allocated using this allocator
770 // printf("gBreakpadAllocator allocated size = %d\n",
771 // gBreakpadAllocator->GetAllocatedSize() );
773 return (BreakpadRef)breakpad;
778 } catch(...) { // don't let exceptions leave this C API
779 fprintf(stderr, "BreakpadCreate() : error\n");
782 if (gKeyValueAllocator) {
783 gKeyValueAllocator->~ProtectedMemoryAllocator();
784 gKeyValueAllocator = NULL;
787 if (gBreakpadAllocator) {
788 gBreakpadAllocator->~ProtectedMemoryAllocator();
789 gBreakpadAllocator = NULL;
792 delete gMasterAllocator;
793 gMasterAllocator = NULL;
798 //=============================================================================
799 void BreakpadRelease(BreakpadRef ref) {
801 Breakpad *breakpad = (Breakpad *)ref;
803 if (gMasterAllocator) {
804 gMasterAllocator->Unprotect();
805 gKeyValueAllocator->Unprotect();
806 gBreakpadAllocator->Unprotect();
808 breakpad->~Breakpad();
810 // Unfortunately, it's not possible to deallocate this stuff
811 // because the exception handling thread is still finishing up
812 // asynchronously at this point... OK, it could be done with
813 // locks, etc. But since BreakpadRelease() should usually only
814 // be called right before the process exits, it's not worth
815 // deallocating this stuff.
817 gKeyValueAllocator->~ProtectedMemoryAllocator();
818 gBreakpadAllocator->~ProtectedMemoryAllocator();
819 delete gMasterAllocator;
821 gMasterAllocator = NULL;
822 gKeyValueAllocator = NULL;
823 gBreakpadAllocator = NULL;
826 pthread_mutex_destroy(&gDictionaryMutex);
828 } catch(...) { // don't let exceptions leave this C API
829 fprintf(stderr, "BreakpadRelease() : error\n");
833 //=============================================================================
834 void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
836 // Not called at exception time
837 Breakpad *breakpad = (Breakpad *)ref;
839 if (breakpad && key && gKeyValueAllocator) {
840 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
842 breakpad->SetKeyValue(key, value);
844 } catch(...) { // don't let exceptions leave this C API
845 fprintf(stderr, "BreakpadSetKeyValue() : error\n");
849 void BreakpadAddUploadParameter(BreakpadRef ref,
852 // The only difference, internally, between an upload parameter and
853 // a key value one that is set with BreakpadSetKeyValue is that we
854 // prepend the keyname with a special prefix. This informs the
855 // crash sender that the parameter should be sent along with the
856 // POST of the crash dump upload.
858 Breakpad *breakpad = (Breakpad *)ref;
860 if (breakpad && key && gKeyValueAllocator) {
861 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
863 NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
864 stringByAppendingString:key];
865 breakpad->SetKeyValue(prefixedKey, value);
867 } catch(...) { // don't let exceptions leave this C API
868 fprintf(stderr, "BreakpadSetKeyValue() : error\n");
872 void BreakpadRemoveUploadParameter(BreakpadRef ref,
875 // Not called at exception time
876 Breakpad *breakpad = (Breakpad *)ref;
878 if (breakpad && key && gKeyValueAllocator) {
879 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
881 NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
882 @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
883 breakpad->RemoveKeyValue(prefixedKey);
885 } catch(...) { // don't let exceptions leave this C API
886 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
889 //=============================================================================
890 NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
891 NSString *value = nil;
894 // Not called at exception time
895 Breakpad *breakpad = (Breakpad *)ref;
897 if (!breakpad || !key || !gKeyValueAllocator)
900 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
902 value = breakpad->KeyValue(key);
903 } catch(...) { // don't let exceptions leave this C API
904 fprintf(stderr, "BreakpadKeyValue() : error\n");
910 //=============================================================================
911 void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
913 // Not called at exception time
914 Breakpad *breakpad = (Breakpad *)ref;
916 if (breakpad && key && gKeyValueAllocator) {
917 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
919 breakpad->RemoveKeyValue(key);
921 } catch(...) { // don't let exceptions leave this C API
922 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
926 //=============================================================================
927 void BreakpadGenerateAndSendReport(BreakpadRef ref) {
929 Breakpad *breakpad = (Breakpad *)ref;
931 if (breakpad && gKeyValueAllocator) {
932 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
934 gBreakpadAllocator->Unprotect();
935 breakpad->GenerateAndSendReport();
936 gBreakpadAllocator->Protect();
938 } catch(...) { // don't let exceptions leave this C API
939 fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
943 //=============================================================================
944 void BreakpadSetFilterCallback(BreakpadRef ref,
945 BreakpadFilterCallback callback,
949 Breakpad *breakpad = (Breakpad *)ref;
951 if (breakpad && gBreakpadAllocator) {
952 // share the dictionary mutex here (we really don't need a mutex)
953 ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator);
955 breakpad->SetFilterCallback(callback, context);
957 } catch(...) { // don't let exceptions leave this C API
958 fprintf(stderr, "BreakpadSetFilterCallback() : error\n");
962 //============================================================================
963 void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) {
964 int logFileCounter = 0;
966 NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
967 @BREAKPAD_LOGFILE_KEY_PREFIX,
970 NSString *existingLogFilename = nil;
971 existingLogFilename = BreakpadKeyValue(ref, logFileKey);
972 // Find the first log file key that we can use by testing for existence
973 while (existingLogFilename) {
974 if ([existingLogFilename isEqualToString:logPathname]) {
978 logFileKey = [NSString stringWithFormat:@"%@%d",
979 @BREAKPAD_LOGFILE_KEY_PREFIX,
981 existingLogFilename = BreakpadKeyValue(ref, logFileKey);
984 BreakpadSetKeyValue(ref, logFileKey, logPathname);