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