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