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