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