Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / breakpad / src / client / mac / Framework / Breakpad.mm
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 //
30
31
32 #define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
33
34 #import "client/mac/Framework/Breakpad.h"
35
36 #include <assert.h>
37 #import <Foundation/Foundation.h>
38 #include <pthread.h>
39 #include <sys/stat.h>
40 #include <sys/sysctl.h>
41
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"
49
50 #ifndef __EXCEPTIONS
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.
56 #undef try
57 #define try       if (true)
58 #undef catch
59 #define catch(X)  if (false)
60 #endif  // __EXCEPTIONS
61
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;
67
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
73 // stack.
74 //
75 // NOTE: Any memory allocations which are not used during the exception
76 // handling process may be allocated in the normal ways.
77 //
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();
82 //
83 ProtectedMemoryAllocator *gMasterAllocator = NULL;
84 ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
85 ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
86
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;
91
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 {
99  public:
100   ProtectedMemoryLocker(pthread_mutex_t *mutex,
101                         ProtectedMemoryAllocator *allocator)
102       : mutex_(mutex),
103         allocator_(allocator) {
104     // Lock the mutex
105     __attribute__((unused)) int rv = pthread_mutex_lock(mutex_);
106     assert(rv == 0);
107
108     // Unprotect the memory
109     allocator_->Unprotect();
110   }
111
112   ~ProtectedMemoryLocker() {
113     // First protect the memory
114     allocator_->Protect();
115
116     // Then unlock the mutex
117     __attribute__((unused)) int rv = pthread_mutex_unlock(mutex_);
118     assert(rv == 0);
119   };
120
121  private:
122   ProtectedMemoryLocker();
123   ProtectedMemoryLocker(const ProtectedMemoryLocker&);
124   ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&);
125
126   pthread_mutex_t           *mutex_;
127   ProtectedMemoryAllocator  *allocator_;
128 };
129
130 //=============================================================================
131 class Breakpad {
132  public:
133   // factory method
134   static Breakpad *Create(NSDictionary *parameters) {
135     // Allocate from our special allocation pool
136     Breakpad *breakpad =
137       new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
138         Breakpad();
139
140     if (!breakpad)
141       return NULL;
142
143     if (!breakpad->Initialize(parameters)) {
144       // Don't use operator delete() here since we allocated from special pool
145       breakpad->~Breakpad();
146       return NULL;
147     }
148
149     return breakpad;
150   }
151
152   ~Breakpad();
153
154   void SetKeyValue(NSString *key, NSString *value);
155   NSString *KeyValue(NSString *key);
156   void RemoveKeyValue(NSString *key);
157
158   void GenerateAndSendReport();
159
160   void SetFilterCallback(BreakpadFilterCallback callback, void *context) {
161     filter_callback_ = callback;
162     filter_callback_context_ = context;
163   }
164
165  private:
166   Breakpad()
167     : handler_(NULL),
168       config_params_(NULL),
169       send_and_exit_(true),
170       filter_callback_(NULL),
171       filter_callback_context_(NULL) {
172     inspector_path_[0] = 0;
173   }
174
175   bool Initialize(NSDictionary *parameters);
176
177   bool ExtractParameters(NSDictionary *parameters);
178
179   // Dispatches to HandleException()
180   static bool ExceptionHandlerDirectCallback(void *context,
181                                              int exception_type,
182                                              int exception_code,
183                                              int exception_subcode,
184                                              mach_port_t crashing_thread);
185
186   bool HandleException(int exception_type,
187                        int exception_code,
188                        int exception_subcode,
189                        mach_port_t crashing_thread);
190
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)
194
195   char                    inspector_path_[PATH_MAX];  // Path to inspector tool
196
197   SimpleStringDictionary  *config_params_; // Create parameters (STRONG)
198
199   OnDemandServer          inspector_;
200
201   bool                    send_and_exit_;  // Exit after sending, if true
202
203   BreakpadFilterCallback  filter_callback_;
204   void                    *filter_callback_context_;
205 };
206
207 #pragma mark -
208 #pragma mark Helper functions
209
210 //=============================================================================
211 // Helper functions
212
213 //=============================================================================
214 static BOOL IsDebuggerActive() {
215   BOOL result = NO;
216   NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
217
218   // We check both defaults and the environment variable here
219
220   BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
221
222   if (!ignoreDebugger) {
223     char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
224     ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
225   }
226
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);
231     size_t actualSize;
232
233     if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
234       struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
235
236       if (info) {
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;
240
241         free(info);
242       }
243     }
244   }
245
246   return result;
247 }
248
249 //=============================================================================
250 bool Breakpad::ExceptionHandlerDirectCallback(void *context,
251                                                     int exception_type,
252                                                     int exception_code,
253                                                     int exception_subcode,
254                                                     mach_port_t crashing_thread) {
255   Breakpad *breakpad = (Breakpad *)context;
256
257   // If our context is damaged or something, just return false to indicate that
258   // the handler should continue without us.
259   if (!breakpad)
260     return false;
261
262   return breakpad->HandleException( exception_type,
263                                     exception_code,
264                                     exception_subcode,
265                                     crashing_thread);
266 }
267
268 //=============================================================================
269 #pragma mark -
270
271 #include <dlfcn.h>
272
273 //=============================================================================
274 // Returns the pathname to the Resources directory for this version of
275 // Breakpad which we are now running.
276 //
277 // Don't make the function static, since _dyld_lookup_and_bind_fully needs a
278 // simple non-static C name
279 //
280 extern "C" {
281 NSString * GetResourcePath();
282 NSString * GetResourcePath() {
283   NSString *resourcePath = nil;
284
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
289   //
290
291   // Get the pathname to the code which contains this function
292   Dl_info info;
293   if (dladdr((const void*)GetResourcePath, &info) != 0) {
294     NSFileManager *filemgr = [NSFileManager defaultManager];
295     NSString *filePath =
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/"];
302   } else {
303     // fallback plan
304     NSBundle *bundle =
305         [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
306     resourcePath = [bundle resourcePath];
307   }
308
309   return resourcePath;
310 }
311 }  // extern "C"
312
313 //=============================================================================
314 bool Breakpad::Initialize(NSDictionary *parameters) {
315   // Initialize
316   config_params_ = NULL;
317   handler_ = NULL;
318
319   // Check for debugger
320   if (IsDebuggerActive()) {
321     return true;
322   }
323
324   // Gather any user specified parameters
325   if (!ExtractParameters(parameters)) {
326     return false;
327   }
328
329   // Get path to Inspector executable.
330   NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION);
331
332   // Standardize path (resolve symlinkes, etc.)  and escape spaces
333   inspectorPathString = [inspectorPathString stringByStandardizingPath];
334   inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "]
335                                               componentsJoinedByString:@"\\ "];
336
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"
343
344   name_t portName;
345   snprintf(portName, sizeof(name_t),  "%s%d", RECEIVE_PORT_NAME, getpid());
346
347   // Save the location of the Inspector
348   strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation],
349           sizeof(inspector_path_));
350
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_));
357
358   kern_return_t kr = inspector_.Initialize(inspector_path_,
359                                            portName,
360                                            true);        // shutdown on exit
361
362   if (kr != KERN_SUCCESS) {
363     return false;
364   }
365
366   // Create the handler (allocating it in our special protected pool)
367   handler_ =
368       new (gBreakpadAllocator->Allocate(
369           sizeof(google_breakpad::ExceptionHandler)))
370           google_breakpad::ExceptionHandler(
371               Breakpad::ExceptionHandlerDirectCallback, this, true);
372   return true;
373 }
374
375 //=============================================================================
376 Breakpad::~Breakpad() {
377   // Note that we don't use operator delete() on these pointers,
378   // since they were allocated by ProtectedMemoryAllocator objects.
379   //
380   if (config_params_) {
381     config_params_->~SimpleStringDictionary();
382   }
383
384   if (handler_)
385     handler_->~ExceptionHandler();
386 }
387
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];
393
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];
411   NSString *vendor =
412       [parameters objectForKey:@BREAKPAD_VENDOR];
413   NSString *dumpSubdirectory =
414       [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
415
416   NSDictionary *serverParameters =
417       [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
418
419   // These may have been set above as user prefs, which take priority.
420   if (!skipConfirm) {
421     skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
422   }
423   if (!sendAndExit) {
424     sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
425   }
426
427   if (!product)
428     product = [parameters objectForKey:@"CFBundleName"];
429
430   if (!display) {
431     display = [parameters objectForKey:@"CFBundleDisplayName"];
432     if (!display) {
433       display = product;
434     }
435   }
436
437   if (!version)
438     version = [parameters objectForKey:@"CFBundleVersion"];
439
440   if (!interval)
441     interval = @"3600";
442
443   if (!timeout)
444     timeout = @"300";
445
446   if (!logFileTailSize)
447     logFileTailSize = @"200000";
448
449   if (!vendor) {
450     vendor = @"Vendor not specified";
451   }
452
453   // Normalize the values.
454   if (skipConfirm) {
455     skipConfirm = [skipConfirm uppercaseString];
456
457     if ([skipConfirm isEqualToString:@"YES"] ||
458         [skipConfirm isEqualToString:@"TRUE"] ||
459         [skipConfirm isEqualToString:@"1"])
460       skipConfirm = @"YES";
461     else
462       skipConfirm = @"NO";
463   } else {
464     skipConfirm = @"NO";
465   }
466
467   send_and_exit_ = true;
468   if (sendAndExit) {
469     sendAndExit = [sendAndExit uppercaseString];
470
471     if ([sendAndExit isEqualToString:@"NO"] ||
472         [sendAndExit isEqualToString:@"FALSE"] ||
473         [sendAndExit isEqualToString:@"0"])
474       send_and_exit_ = false;
475   }
476
477   if (requestUserText) {
478     requestUserText = [requestUserText uppercaseString];
479
480     if ([requestUserText isEqualToString:@"YES"] ||
481         [requestUserText isEqualToString:@"TRUE"] ||
482         [requestUserText isEqualToString:@"1"])
483       requestUserText = @"YES";
484     else
485       requestUserText = @"NO";
486   } else {
487     requestUserText = @"NO";
488   }
489
490   // Find the helper applications if not specified in user config.
491   NSString *resourcePath = nil;
492   if (!inspectorPathString || !reporterPathString) {
493     resourcePath = GetResourcePath();
494     if (!resourcePath) {
495       return false;
496     }
497   }
498
499   // Find Inspector.
500   if (!inspectorPathString) {
501     inspectorPathString =
502         [resourcePath stringByAppendingPathComponent:@"Inspector"];
503   }
504
505   // Verify that there is an Inspector tool.
506   if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
507     return false;
508   }
509
510   // Find Reporter.
511   if (!reporterPathString) {
512     reporterPathString =
513         [resourcePath
514          stringByAppendingPathComponent:@"crash_report_sender.app"];
515     reporterPathString =
516         [[NSBundle bundleWithPath:reporterPathString] executablePath];
517   }
518
519   // Verify that there is a Reporter application.
520   if (![[NSFileManager defaultManager]
521              fileExistsAtPath:reporterPathString]) {
522     return false;
523   }
524
525   if (!dumpSubdirectory) {
526     dumpSubdirectory = @"";
527   }
528
529   // The product, version, and URL are required values.
530   if (![product length]) {
531     return false;
532   }
533
534   if (![version length]) {
535     return false;
536   }
537
538   if (![urlStr length]) {
539     return false;
540   }
541
542   config_params_ =
543       new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
544         SimpleStringDictionary();
545
546   SimpleStringDictionary &dictionary = *config_params_;
547
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]);
568
569   struct timeval tv;
570   gettimeofday(&tv, NULL);
571   char timeStartedString[32];
572   sprintf(timeStartedString, "%zd", tv.tv_sec);
573   dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME,
574                          timeStartedString);
575
576   if (logFilePaths) {
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]);
583     }
584   }
585
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]);
593     }
594   }
595   return true;
596 }
597
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)
602     return;
603
604   config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
605 }
606
607 //=============================================================================
608 NSString *Breakpad::KeyValue(NSString *key) {
609   if (!config_params_ || !key)
610     return nil;
611
612   const char *value = config_params_->GetValueForKey([key UTF8String]);
613   return value ? [NSString stringWithUTF8String:value] : nil;
614 }
615
616 //=============================================================================
617 void Breakpad::RemoveKeyValue(NSString *key) {
618   if (!config_params_ || !key) return;
619
620   config_params_->RemoveKey([key UTF8String]);
621 }
622
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");
628 }
629
630 //=============================================================================
631 bool Breakpad::HandleException(int exception_type,
632                                int exception_code,
633                                int exception_subcode,
634                                mach_port_t crashing_thread) {
635   if (filter_callback_) {
636     bool should_handle = filter_callback_(exception_type,
637                                           exception_code,
638                                           crashing_thread,
639                                           filter_callback_context_);
640     if (!should_handle) return false;
641   }
642
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();
652
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;
656
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
663
664   InspectorInfo info;
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));
670
671   MachPortSender sender(inspector_.GetServicePort());
672
673   kern_return_t result = sender.SendMessage(message, 2000);
674
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_);
679
680     while ( (entry = iter.Next()) ) {
681       KeyValueMessageData keyvalue_data(*entry);
682
683       MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair);
684       keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data));
685
686       result = sender.SendMessage(keyvalue_message, 2000);
687
688       if (result != KERN_SUCCESS) {
689         break;
690       }
691     }
692
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);
697     }
698   }
699
700 #if VERBOSE
701   PRINT_MACH_RESULT(result, "Breakpad: SendMessage ");
702   printf("Breakpad: Inspector service port = %#x\n",
703     inspector_.GetServicePort());
704 #endif
705
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;
709
710   return false;
711 }
712
713 //=============================================================================
714 //=============================================================================
715
716 #pragma mark -
717 #pragma mark Public API
718
719 //=============================================================================
720 BreakpadRef BreakpadCreate(NSDictionary *parameters) {
721   try {
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.
726     //
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.
729     //
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.
733     gMasterAllocator =
734         new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
735
736     gKeyValueAllocator =
737         new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
738             ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
739
740     // Create a mutex for use in accessing the SimpleStringDictionary
741     int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
742     if (mutexResult == 0) {
743
744       // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
745       // Let's round up to the nearest page size.
746       //
747       int breakpad_pool_size = 4096;
748
749       /*
750        sizeof(Breakpad)
751        + sizeof(google_breakpad::ExceptionHandler)
752        + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
753        */
754
755       gBreakpadAllocator =
756           new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
757               ProtectedMemoryAllocator(breakpad_pool_size);
758
759       // Stack-based autorelease pool for Breakpad::Create() obj-c code.
760       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
761       Breakpad *breakpad = Breakpad::Create(parameters);
762
763       if (breakpad) {
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() );
772         [pool release];
773         return (BreakpadRef)breakpad;
774       }
775
776       [pool release];
777     }
778   } catch(...) {    // don't let exceptions leave this C API
779     fprintf(stderr, "BreakpadCreate() : error\n");
780   }
781
782   if (gKeyValueAllocator) {
783     gKeyValueAllocator->~ProtectedMemoryAllocator();
784     gKeyValueAllocator = NULL;
785   }
786
787   if (gBreakpadAllocator) {
788     gBreakpadAllocator->~ProtectedMemoryAllocator();
789     gBreakpadAllocator = NULL;
790   }
791
792   delete gMasterAllocator;
793   gMasterAllocator = NULL;
794
795   return NULL;
796 }
797
798 //=============================================================================
799 void BreakpadRelease(BreakpadRef ref) {
800   try {
801     Breakpad *breakpad = (Breakpad *)ref;
802
803     if (gMasterAllocator) {
804       gMasterAllocator->Unprotect();
805       gKeyValueAllocator->Unprotect();
806       gBreakpadAllocator->Unprotect();
807
808       breakpad->~Breakpad();
809
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.
816 #if 0
817       gKeyValueAllocator->~ProtectedMemoryAllocator();
818       gBreakpadAllocator->~ProtectedMemoryAllocator();
819       delete gMasterAllocator;
820
821       gMasterAllocator = NULL;
822       gKeyValueAllocator = NULL;
823       gBreakpadAllocator = NULL;
824 #endif
825
826       pthread_mutex_destroy(&gDictionaryMutex);
827     }
828   } catch(...) {    // don't let exceptions leave this C API
829     fprintf(stderr, "BreakpadRelease() : error\n");
830   }
831 }
832
833 //=============================================================================
834 void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
835   try {
836     // Not called at exception time
837     Breakpad *breakpad = (Breakpad *)ref;
838
839     if (breakpad && key && gKeyValueAllocator) {
840       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
841
842       breakpad->SetKeyValue(key, value);
843     }
844   } catch(...) {    // don't let exceptions leave this C API
845     fprintf(stderr, "BreakpadSetKeyValue() : error\n");
846   }
847 }
848
849 void BreakpadAddUploadParameter(BreakpadRef ref,
850                                 NSString *key,
851                                 NSString *value) {
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.
857   try {
858     Breakpad *breakpad = (Breakpad *)ref;
859
860     if (breakpad && key && gKeyValueAllocator) {
861       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
862
863       NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
864                                 stringByAppendingString:key];
865       breakpad->SetKeyValue(prefixedKey, value);
866     }
867   } catch(...) {    // don't let exceptions leave this C API
868     fprintf(stderr, "BreakpadSetKeyValue() : error\n");
869   }
870 }
871
872 void BreakpadRemoveUploadParameter(BreakpadRef ref,
873                                    NSString *key) {
874   try {
875     // Not called at exception time
876     Breakpad *breakpad = (Breakpad *)ref;
877
878     if (breakpad && key && gKeyValueAllocator) {
879       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
880
881       NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
882                                         @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
883       breakpad->RemoveKeyValue(prefixedKey);
884     }
885   } catch(...) {    // don't let exceptions leave this C API
886     fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
887   }
888 }
889 //=============================================================================
890 NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
891   NSString *value = nil;
892
893   try {
894     // Not called at exception time
895     Breakpad *breakpad = (Breakpad *)ref;
896
897     if (!breakpad || !key || !gKeyValueAllocator)
898       return nil;
899
900     ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
901
902     value = breakpad->KeyValue(key);
903   } catch(...) {    // don't let exceptions leave this C API
904     fprintf(stderr, "BreakpadKeyValue() : error\n");
905   }
906
907   return value;
908 }
909
910 //=============================================================================
911 void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
912   try {
913     // Not called at exception time
914     Breakpad *breakpad = (Breakpad *)ref;
915
916     if (breakpad && key && gKeyValueAllocator) {
917       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
918
919       breakpad->RemoveKeyValue(key);
920     }
921   } catch(...) {    // don't let exceptions leave this C API
922     fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
923   }
924 }
925
926 //=============================================================================
927 void BreakpadGenerateAndSendReport(BreakpadRef ref) {
928   try {
929     Breakpad *breakpad = (Breakpad *)ref;
930
931     if (breakpad && gKeyValueAllocator) {
932       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
933
934       gBreakpadAllocator->Unprotect();
935       breakpad->GenerateAndSendReport();
936       gBreakpadAllocator->Protect();
937     }
938   } catch(...) {    // don't let exceptions leave this C API
939     fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
940   }
941 }
942
943 //=============================================================================
944 void BreakpadSetFilterCallback(BreakpadRef ref,
945                                BreakpadFilterCallback callback,
946                                void *context) {
947
948   try {
949     Breakpad *breakpad = (Breakpad *)ref;
950
951     if (breakpad && gBreakpadAllocator) {
952       // share the dictionary mutex here (we really don't need a mutex)
953       ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator);
954
955       breakpad->SetFilterCallback(callback, context);
956     }
957   } catch(...) {    // don't let exceptions leave this C API
958     fprintf(stderr, "BreakpadSetFilterCallback() : error\n");
959   }
960 }
961
962 //============================================================================
963 void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) {
964   int logFileCounter = 0;
965
966   NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
967                                    @BREAKPAD_LOGFILE_KEY_PREFIX,
968                                    logFileCounter];
969
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]) {
975       return;
976     }
977     logFileCounter++;
978     logFileKey = [NSString stringWithFormat:@"%@%d",
979                            @BREAKPAD_LOGFILE_KEY_PREFIX,
980                            logFileCounter];
981     existingLogFilename = BreakpadKeyValue(ref, logFileKey);
982   }
983
984   BreakpadSetKeyValue(ref, logFileKey, logPathname);
985 }