PageRenderTime 25ms CodeModel.GetById 15ms app.highlight 4ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/client/mac/Framework/Breakpad.mm

http://github.com/tomahawk-player/tomahawk
Objective C++ | 995 lines | 627 code | 173 blank | 195 comment | 101 complexity | 98a78469ce74f182ced4d2017743f856 MD5 | raw file
  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#define VERBOSE 0
 32
 33#if VERBOSE
 34  static bool gDebugLog = true;
 35#else
 36  static bool gDebugLog = false;
 37#endif
 38
 39#define DEBUGLOG if (gDebugLog) fprintf
 40#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
 41
 42#import "client/mac/Framework/Breakpad.h"
 43
 44#import <Foundation/Foundation.h>
 45#import <sys/stat.h>
 46#import <sys/sysctl.h>
 47
 48#import "client/mac/crash_generation/Inspector.h"
 49#import "client/mac/handler/exception_handler.h"
 50#import "client/mac/Framework/Breakpad.h"
 51#import "client/mac/Framework/OnDemandServer.h"
 52#import "client/mac/handler/protected_memory_allocator.h"
 53#import "common/mac/MachIPC.h"
 54#import "common/mac/SimpleStringDictionary.h"
 55
 56using google_breakpad::KeyValueEntry;
 57using google_breakpad::MachPortSender;
 58using google_breakpad::MachReceiveMessage;
 59using google_breakpad::MachSendMessage;
 60using google_breakpad::ReceivePort;
 61using google_breakpad::SimpleStringDictionary;
 62using google_breakpad::SimpleStringDictionaryIterator;
 63
 64//=============================================================================
 65// We want any memory allocations which are used by breakpad during the
 66// exception handling process (after a crash has happened) to be read-only
 67// to prevent them from being smashed before a crash occurs.  Unfortunately
 68// we cannot protect against smashes to our exception handling thread's
 69// stack.
 70//
 71// NOTE: Any memory allocations which are not used during the exception
 72// handling process may be allocated in the normal ways.
 73//
 74// The ProtectedMemoryAllocator class provides an Allocate() method which
 75// we'll using in conjunction with placement operator new() to control
 76// allocation of C++ objects.  Note that we don't use operator delete()
 77// but instead call the objects destructor directly:  object->~ClassName();
 78//
 79ProtectedMemoryAllocator *gMasterAllocator = NULL;
 80ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
 81ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
 82
 83// Mutex for thread-safe access to the key/value dictionary used by breakpad.
 84// It's a global instead of an instance variable of Breakpad
 85// since it can't live in a protected memory area.
 86pthread_mutex_t gDictionaryMutex;
 87
 88//=============================================================================
 89// Stack-based object for thread-safe access to a memory-protected region.
 90// It's assumed that normally the memory block (allocated by the allocator)
 91// is protected (read-only).  Creating a stack-based instance of
 92// ProtectedMemoryLocker will unprotect this block after taking the lock.
 93// Its destructor will first re-protect the memory then release the lock.
 94class ProtectedMemoryLocker {
 95public:
 96  // allocator may be NULL, in which case no Protect() or Unprotect() calls
 97  // will be made, but a lock will still be taken
 98  ProtectedMemoryLocker(pthread_mutex_t *mutex,
 99                        ProtectedMemoryAllocator *allocator)
100  : mutex_(mutex), allocator_(allocator) {
101    // Lock the mutex
102    assert(pthread_mutex_lock(mutex_) == 0);
103
104    // Unprotect the memory
105    if (allocator_ ) {
106      allocator_->Unprotect();
107    }
108  }
109
110  ~ProtectedMemoryLocker() {
111    // First protect the memory
112    if (allocator_) {
113      allocator_->Protect();
114    }
115
116    // Then unlock the mutex
117    assert(pthread_mutex_unlock(mutex_) == 0);
118  };
119
120private:
121  //  Keep anybody from ever creating one of these things not on the stack.
122  ProtectedMemoryLocker() { }
123  ProtectedMemoryLocker(const ProtectedMemoryLocker&);
124  ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&);
125
126  pthread_mutex_t           *mutex_;
127  ProtectedMemoryAllocator  *allocator_;
128};
129
130//=============================================================================
131class 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//=============================================================================
214static 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//=============================================================================
250bool 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//
280extern "C" {
281NSString * GetResourcePath();
282NSString * 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    DEBUGLOG(stderr, "Could not find GetResourcePath\n");
304    // fallback plan
305    NSBundle *bundle =
306        [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
307    resourcePath = [bundle resourcePath];
308  }
309
310  return resourcePath;
311}
312}  // extern "C"
313
314//=============================================================================
315bool Breakpad::Initialize(NSDictionary *parameters) {
316  // Initialize
317  config_params_ = NULL;
318  handler_ = NULL;
319
320  // Check for debugger
321  if (IsDebuggerActive()) {
322    DEBUGLOG(stderr, "Debugger is active:  Not installing handler\n");
323    return true;
324  }
325
326  // Gather any user specified parameters
327  if (!ExtractParameters(parameters)) {
328    return false;
329  }
330
331  // Get path to Inspector executable.
332  NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION);
333
334  // Standardize path (resolve symlinkes, etc.)  and escape spaces
335  inspectorPathString = [inspectorPathString stringByStandardizingPath];
336  inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "]
337                                              componentsJoinedByString:@"\\ "];
338
339  // Create an on-demand server object representing the Inspector.
340  // In case of a crash, we simply need to call the LaunchOnDemand()
341  // method on it, then send a mach message to its service port.
342  // It will then launch and perform a process inspection of our crashed state.
343  // See the HandleException() method for the details.
344#define RECEIVE_PORT_NAME "com.Breakpad.Inspector"
345
346  name_t portName;
347  snprintf(portName, sizeof(name_t),  "%s%d", RECEIVE_PORT_NAME, getpid());
348
349  // Save the location of the Inspector
350  strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation],
351          sizeof(inspector_path_));
352
353  // Append a single command-line argument to the Inspector path
354  // representing the bootstrap name of the launch-on-demand receive port.
355  // When the Inspector is launched, it can use this to lookup the port
356  // by calling bootstrap_check_in().
357  strlcat(inspector_path_, " ", sizeof(inspector_path_));
358  strlcat(inspector_path_, portName, sizeof(inspector_path_));
359
360  kern_return_t kr = inspector_.Initialize(inspector_path_,
361                                           portName,
362                                           true);        // shutdown on exit
363
364  if (kr != KERN_SUCCESS) {
365    return false;
366  }
367
368  // Create the handler (allocating it in our special protected pool)
369  handler_ =
370      new (gBreakpadAllocator->Allocate(
371          sizeof(google_breakpad::ExceptionHandler)))
372          google_breakpad::ExceptionHandler(
373              Breakpad::ExceptionHandlerDirectCallback, this, true);
374  return true;
375}
376
377//=============================================================================
378Breakpad::~Breakpad() {
379  // Note that we don't use operator delete() on these pointers,
380  // since they were allocated by ProtectedMemoryAllocator objects.
381  //
382  if (config_params_) {
383    config_params_->~SimpleStringDictionary();
384  }
385
386  if (handler_)
387    handler_->~ExceptionHandler();
388}
389
390//=============================================================================
391bool Breakpad::ExtractParameters(NSDictionary *parameters) {
392  NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
393  NSString *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM];
394  NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT];
395
396  NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
397  NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
398  NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
399  NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
400  NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
401  NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
402  NSString *inspectorPathString =
403      [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
404  NSString *reporterPathString =
405      [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
406  NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
407  NSArray  *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
408  NSString *logFileTailSize =
409      [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
410  NSString *requestUserText =
411      [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
412  NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
413  NSString *vendor =
414      [parameters objectForKey:@BREAKPAD_VENDOR];
415  NSString *dumpSubdirectory =
416      [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
417
418  NSDictionary *serverParameters =
419      [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
420
421  // These may have been set above as user prefs, which take priority.
422  if (!skipConfirm) {
423    skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
424  }
425  if (!sendAndExit) {
426    sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
427  }
428
429  if (!product)
430    product = [parameters objectForKey:@"CFBundleName"];
431
432  if (!display) {
433    display = [parameters objectForKey:@"CFBundleDisplayName"];
434    if (!display) {
435      display = product;
436    }
437  }
438
439  if (!version)
440    version = [parameters objectForKey:@"CFBundleVersion"];
441
442  if (!interval)
443    interval = @"3600";
444
445  if (!timeout)
446    timeout = @"300";
447
448  if (!logFileTailSize)
449    logFileTailSize = @"200000";
450
451  if (!vendor) {
452    vendor = @"Vendor not specified";
453  }
454
455  // Normalize the values.
456  if (skipConfirm) {
457    skipConfirm = [skipConfirm uppercaseString];
458
459    if ([skipConfirm isEqualToString:@"YES"] ||
460        [skipConfirm isEqualToString:@"TRUE"] ||
461        [skipConfirm isEqualToString:@"1"])
462      skipConfirm = @"YES";
463    else
464      skipConfirm = @"NO";
465  } else {
466    skipConfirm = @"NO";
467  }
468
469  send_and_exit_ = true;
470  if (sendAndExit) {
471    sendAndExit = [sendAndExit uppercaseString];
472
473    if ([sendAndExit isEqualToString:@"NO"] ||
474        [sendAndExit isEqualToString:@"FALSE"] ||
475        [sendAndExit isEqualToString:@"0"])
476      send_and_exit_ = false;
477  }
478
479  if (requestUserText) {
480    requestUserText = [requestUserText uppercaseString];
481
482    if ([requestUserText isEqualToString:@"YES"] ||
483        [requestUserText isEqualToString:@"TRUE"] ||
484        [requestUserText isEqualToString:@"1"])
485      requestUserText = @"YES";
486    else
487      requestUserText = @"NO";
488  } else {
489    requestUserText = @"NO";
490  }
491
492  // Find the helper applications if not specified in user config.
493  NSString *resourcePath = nil;
494  if (!inspectorPathString || !reporterPathString) {
495    resourcePath = GetResourcePath();
496    if (!resourcePath) {
497      DEBUGLOG(stderr, "Could not get resource path\n");
498      return false;
499    }
500  }
501
502  // Find Inspector.
503  if (!inspectorPathString) {
504    inspectorPathString =
505        [resourcePath stringByAppendingPathComponent:@"Inspector"];
506  }
507
508  // Verify that there is an Inspector tool.
509  if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
510    DEBUGLOG(stderr, "Cannot find Inspector tool\n");
511    return false;
512  }
513
514  // Find Reporter.
515  if (!reporterPathString) {
516    reporterPathString =
517        [resourcePath
518         stringByAppendingPathComponent:@"crash_report_sender.app"];
519    reporterPathString =
520        [[NSBundle bundleWithPath:reporterPathString] executablePath];
521  }
522
523  // Verify that there is a Reporter application.
524  if (![[NSFileManager defaultManager]
525             fileExistsAtPath:reporterPathString]) {
526    DEBUGLOG(stderr, "Cannot find Reporter tool\n");
527    return false;
528  }
529
530  if (!dumpSubdirectory) {
531    dumpSubdirectory = @"";
532  }
533
534  // The product, version, and URL are required values.
535  if (![product length]) {
536    DEBUGLOG(stderr, "Missing required product key.\n");
537    return false;
538  }
539
540  if (![version length]) {
541    DEBUGLOG(stderr, "Missing required version key.\n");
542    return false;
543  }
544
545  if (![urlStr length]) {
546    DEBUGLOG(stderr, "Missing required URL key.\n");
547    return false;
548  }
549
550  config_params_ =
551      new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
552        SimpleStringDictionary();
553
554  SimpleStringDictionary &dictionary = *config_params_;
555
556  dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE,     [serverType UTF8String]);
557  dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
558  dictionary.SetKeyValue(BREAKPAD_PRODUCT,         [product UTF8String]);
559  dictionary.SetKeyValue(BREAKPAD_VERSION,         [version UTF8String]);
560  dictionary.SetKeyValue(BREAKPAD_URL,             [urlStr UTF8String]);
561  dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]);
562  dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM,    [skipConfirm UTF8String]);
563  dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]);
564  dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION,
565                         [inspectorPathString fileSystemRepresentation]);
566  dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION,
567                         [reporterPathString fileSystemRepresentation]);
568  dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE,
569                         [logFileTailSize UTF8String]);
570  dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
571                         [requestUserText UTF8String]);
572  dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]);
573  dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
574  dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
575                         [dumpSubdirectory UTF8String]);
576
577  struct timeval tv;
578  gettimeofday(&tv, NULL);
579  char timeStartedString[32];
580  sprintf(timeStartedString, "%zd", tv.tv_sec);
581  dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME,
582                         timeStartedString);
583
584  if (logFilePaths) {
585    char logFileKey[255];
586    for(unsigned int i = 0; i < [logFilePaths count]; i++) {
587      sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i);
588      dictionary.SetKeyValue(logFileKey,
589                             [[logFilePaths objectAtIndex:i]
590                               fileSystemRepresentation]);
591    }
592  }
593
594  if (serverParameters) {
595    // For each key-value pair, call BreakpadAddUploadParameter()
596    NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
597    NSString *aParameter;
598    while ((aParameter = [keyEnumerator nextObject])) {
599      BreakpadAddUploadParameter(this, aParameter,
600				 [serverParameters objectForKey:aParameter]);
601    }
602  }
603  return true;
604}
605
606//=============================================================================
607void Breakpad::SetKeyValue(NSString *key, NSString *value) {
608  // We allow nil values. This is the same as removing the keyvalue.
609  if (!config_params_ || !key)
610    return;
611
612  config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
613}
614
615//=============================================================================
616NSString *Breakpad::KeyValue(NSString *key) {
617  if (!config_params_ || !key)
618    return nil;
619
620  const char *value = config_params_->GetValueForKey([key UTF8String]);
621  return value ? [NSString stringWithUTF8String:value] : nil;
622}
623
624//=============================================================================
625void Breakpad::RemoveKeyValue(NSString *key) {
626  if (!config_params_ || !key) return;
627
628  config_params_->RemoveKey([key UTF8String]);
629}
630
631//=============================================================================
632void Breakpad::GenerateAndSendReport() {
633  config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES");
634  HandleException(0, 0, 0, mach_thread_self());
635  config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO");
636}
637
638//=============================================================================
639bool Breakpad::HandleException(int exception_type,
640                               int exception_code,
641                               int exception_subcode,
642                               mach_port_t crashing_thread) {
643  DEBUGLOG(stderr, "Breakpad: an exception occurred\n");
644
645  if (filter_callback_) {
646    bool should_handle = filter_callback_(exception_type,
647                                          exception_code,
648                                          crashing_thread,
649                                          filter_callback_context_);
650    if (!should_handle) return false;
651  }
652
653  // We need to reset the memory protections to be read/write,
654  // since LaunchOnDemand() requires changing state.
655  gBreakpadAllocator->Unprotect();
656  // Configure the server to launch when we message the service port.
657  // The reason we do this here, rather than at startup, is that we
658  // can leak a bootstrap service entry if this method is called and
659  // there never ends up being a crash.
660  inspector_.LaunchOnDemand();
661  gBreakpadAllocator->Protect();
662
663  // The Inspector should send a message to this port to verify it
664  // received our information and has finished the inspection.
665  ReceivePort acknowledge_port;
666
667  // Send initial information to the Inspector.
668  MachSendMessage message(kMsgType_InspectorInitialInfo);
669  message.AddDescriptor(mach_task_self());          // our task
670  message.AddDescriptor(crashing_thread);           // crashing thread
671  message.AddDescriptor(mach_thread_self());        // exception-handling thread
672  message.AddDescriptor(acknowledge_port.GetPort());// message receive port
673
674  InspectorInfo info;
675  info.exception_type = exception_type;
676  info.exception_code = exception_code;
677  info.exception_subcode = exception_subcode;
678  info.parameter_count = config_params_->GetCount();
679  message.SetData(&info, sizeof(info));
680
681  MachPortSender sender(inspector_.GetServicePort());
682
683  kern_return_t result = sender.SendMessage(message, 2000);
684
685  if (result == KERN_SUCCESS) {
686    // Now, send a series of key-value pairs to the Inspector.
687    const KeyValueEntry *entry = NULL;
688    SimpleStringDictionaryIterator iter(*config_params_);
689
690    while ( (entry = iter.Next()) ) {
691      KeyValueMessageData keyvalue_data(*entry);
692
693      MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair);
694      keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data));
695
696      result = sender.SendMessage(keyvalue_message, 2000);
697
698      if (result != KERN_SUCCESS) {
699        break;
700      }
701    }
702
703    if (result == KERN_SUCCESS) {
704      // Wait for acknowledgement that the inspection has finished.
705      MachReceiveMessage acknowledge_messsage;
706      result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000);
707    }
708  }
709
710#if VERBOSE
711  PRINT_MACH_RESULT(result, "Breakpad: SendMessage ");
712  printf("Breakpad: Inspector service port = %#x\n",
713    inspector_.GetServicePort());
714#endif
715
716  // If we don't want any forwarding, return true here to indicate that we've
717  // processed things as much as we want.
718  if (send_and_exit_) return true;
719
720  return false;
721}
722
723//=============================================================================
724//=============================================================================
725
726#pragma mark -
727#pragma mark Public API
728
729//=============================================================================
730BreakpadRef BreakpadCreate(NSDictionary *parameters) {
731  try {
732    // This is confusing.  Our two main allocators for breakpad memory are:
733    //    - gKeyValueAllocator for the key/value memory
734    //    - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
735    //      breakpad allocations which are accessed at exception handling time.
736    //
737    // But in order to avoid these two allocators themselves from being smashed,
738    // we'll protect them as well by allocating them with gMasterAllocator.
739    //
740    // gMasterAllocator itself will NOT be protected, but this doesn't matter,
741    // since once it does its allocations and locks the memory, smashes to itself
742    // don't affect anything we care about.
743    gMasterAllocator =
744        new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
745
746    gKeyValueAllocator =
747        new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
748            ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
749
750    // Create a mutex for use in accessing the SimpleStringDictionary
751    int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
752    if (mutexResult == 0) {
753
754      // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
755      // Let's round up to the nearest page size.
756      //
757      int breakpad_pool_size = 4096;
758
759      /*
760       sizeof(Breakpad)
761       + sizeof(google_breakpad::ExceptionHandler)
762       + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
763       */
764
765      gBreakpadAllocator =
766          new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
767              ProtectedMemoryAllocator(breakpad_pool_size);
768
769      // Stack-based autorelease pool for Breakpad::Create() obj-c code.
770      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
771      Breakpad *breakpad = Breakpad::Create(parameters);
772
773      if (breakpad) {
774        // Make read-only to protect against memory smashers
775        gMasterAllocator->Protect();
776        gKeyValueAllocator->Protect();
777        gBreakpadAllocator->Protect();
778        // Can uncomment this line to figure out how much space was actually
779        // allocated using this allocator
780        //     printf("gBreakpadAllocator allocated size = %d\n",
781        //         gBreakpadAllocator->GetAllocatedSize() );
782        [pool release];
783        return (BreakpadRef)breakpad;
784      }
785
786      [pool release];
787    }
788  } catch(...) {    // don't let exceptions leave this C API
789    fprintf(stderr, "BreakpadCreate() : error\n");
790  }
791
792  if (gKeyValueAllocator) {
793    gKeyValueAllocator->~ProtectedMemoryAllocator();
794    gKeyValueAllocator = NULL;
795  }
796
797  if (gBreakpadAllocator) {
798    gBreakpadAllocator->~ProtectedMemoryAllocator();
799    gBreakpadAllocator = NULL;
800  }
801
802  delete gMasterAllocator;
803  gMasterAllocator = NULL;
804
805  return NULL;
806}
807
808//=============================================================================
809void BreakpadRelease(BreakpadRef ref) {
810  try {
811    Breakpad *breakpad = (Breakpad *)ref;
812
813    if (gMasterAllocator) {
814      gMasterAllocator->Unprotect();
815      gKeyValueAllocator->Unprotect();
816      gBreakpadAllocator->Unprotect();
817
818      breakpad->~Breakpad();
819
820      // Unfortunately, it's not possible to deallocate this stuff
821      // because the exception handling thread is still finishing up
822      // asynchronously at this point...  OK, it could be done with
823      // locks, etc.  But since BreakpadRelease() should usually only
824      // be called right before the process exits, it's not worth
825      // deallocating this stuff.
826#if 0
827      gKeyValueAllocator->~ProtectedMemoryAllocator();
828      gBreakpadAllocator->~ProtectedMemoryAllocator();
829      delete gMasterAllocator;
830
831      gMasterAllocator = NULL;
832      gKeyValueAllocator = NULL;
833      gBreakpadAllocator = NULL;
834#endif
835
836      pthread_mutex_destroy(&gDictionaryMutex);
837    }
838  } catch(...) {    // don't let exceptions leave this C API
839    fprintf(stderr, "BreakpadRelease() : error\n");
840  }
841}
842
843//=============================================================================
844void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
845  try {
846    // Not called at exception time
847    Breakpad *breakpad = (Breakpad *)ref;
848
849    if (breakpad && key && gKeyValueAllocator) {
850      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
851
852      breakpad->SetKeyValue(key, value);
853    }
854  } catch(...) {    // don't let exceptions leave this C API
855    fprintf(stderr, "BreakpadSetKeyValue() : error\n");
856  }
857}
858
859void BreakpadAddUploadParameter(BreakpadRef ref,
860                                NSString *key,
861                                NSString *value) {
862  // The only difference, internally, between an upload parameter and
863  // a key value one that is set with BreakpadSetKeyValue is that we
864  // prepend the keyname with a special prefix.  This informs the
865  // crash sender that the parameter should be sent along with the
866  // POST of the crash dump upload.
867  try {
868    Breakpad *breakpad = (Breakpad *)ref;
869
870    if (breakpad && key && gKeyValueAllocator) {
871      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
872
873      NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
874				stringByAppendingString:key];
875      breakpad->SetKeyValue(prefixedKey, value);
876    }
877  } catch(...) {    // don't let exceptions leave this C API
878    fprintf(stderr, "BreakpadSetKeyValue() : error\n");
879  }
880}
881
882void BreakpadRemoveUploadParameter(BreakpadRef ref,
883                                   NSString *key) {
884  try {
885    // Not called at exception time
886    Breakpad *breakpad = (Breakpad *)ref;
887
888    if (breakpad && key && gKeyValueAllocator) {
889      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
890
891      NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
892                                        @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
893      breakpad->RemoveKeyValue(prefixedKey);
894    }
895  } catch(...) {    // don't let exceptions leave this C API
896    fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
897  }
898}
899//=============================================================================
900NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
901  NSString *value = nil;
902
903  try {
904    // Not called at exception time
905    Breakpad *breakpad = (Breakpad *)ref;
906
907    if (!breakpad || !key || !gKeyValueAllocator)
908      return nil;
909
910    ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
911
912    value = breakpad->KeyValue(key);
913  } catch(...) {    // don't let exceptions leave this C API
914    fprintf(stderr, "BreakpadKeyValue() : error\n");
915  }
916
917  return value;
918}
919
920//=============================================================================
921void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
922  try {
923    // Not called at exception time
924    Breakpad *breakpad = (Breakpad *)ref;
925
926    if (breakpad && key && gKeyValueAllocator) {
927      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
928
929      breakpad->RemoveKeyValue(key);
930    }
931  } catch(...) {    // don't let exceptions leave this C API
932    fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
933  }
934}
935
936//=============================================================================
937void BreakpadGenerateAndSendReport(BreakpadRef ref) {
938  try {
939    Breakpad *breakpad = (Breakpad *)ref;
940
941    if (breakpad && gKeyValueAllocator) {
942      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
943
944      gBreakpadAllocator->Unprotect();
945      breakpad->GenerateAndSendReport();
946      gBreakpadAllocator->Protect();
947    }
948  } catch(...) {    // don't let exceptions leave this C API
949    fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
950  }
951}
952
953//=============================================================================
954void BreakpadSetFilterCallback(BreakpadRef ref,
955                               BreakpadFilterCallback callback,
956                               void *context) {
957
958  try {
959    Breakpad *breakpad = (Breakpad *)ref;
960
961    if (breakpad && gBreakpadAllocator) {
962      // share the dictionary mutex here (we really don't need a mutex)
963      ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator);
964
965      breakpad->SetFilterCallback(callback, context);
966    }
967  } catch(...) {    // don't let exceptions leave this C API
968    fprintf(stderr, "BreakpadSetFilterCallback() : error\n");
969  }
970}
971
972//============================================================================
973void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) {
974  int logFileCounter = 0;
975
976  NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
977                                   @BREAKPAD_LOGFILE_KEY_PREFIX,
978                                   logFileCounter];
979
980  NSString *existingLogFilename = nil;
981  existingLogFilename = BreakpadKeyValue(ref, logFileKey);
982  // Find the first log file key that we can use by testing for existence
983  while (existingLogFilename) {
984    if ([existingLogFilename isEqualToString:logPathname]) {
985      return;
986    }
987    logFileCounter++;
988    logFileKey = [NSString stringWithFormat:@"%@%d",
989                           @BREAKPAD_LOGFILE_KEY_PREFIX,
990                           logFileCounter];
991    existingLogFilename = BreakpadKeyValue(ref, logFileKey);
992  }
993
994  BreakpadSetKeyValue(ref, logFileKey, logPathname);
995}