/thirdparty/breakpad/client/mac/handler/exception_handler.cc

http://github.com/tomahawk-player/tomahawk · C++ · 830 lines · 608 code · 108 blank · 114 comment · 107 complexity · 515074fd1e1d5cf85c260113e437aaed MD5 · raw file

  1. // Copyright (c) 2006, Google Inc.
  2. // All rights reserved.
  3. //
  4. // Redistribution and use in source and binary forms, with or without
  5. // modification, are permitted provided that the following conditions are
  6. // met:
  7. //
  8. // * Redistributions of source code must retain the above copyright
  9. // notice, this list of conditions and the following disclaimer.
  10. // * Redistributions in binary form must reproduce the above
  11. // copyright notice, this list of conditions and the following disclaimer
  12. // in the documentation and/or other materials provided with the
  13. // distribution.
  14. // * Neither the name of Google Inc. nor the names of its
  15. // contributors may be used to endorse or promote products derived from
  16. // this software without specific prior written permission.
  17. //
  18. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. #include <map>
  30. #include <mach/exc.h>
  31. #include <mach/mig.h>
  32. #include <pthread.h>
  33. #include <signal.h>
  34. #include <TargetConditionals.h>
  35. #include "client/mac/handler/exception_handler.h"
  36. #include "client/mac/handler/minidump_generator.h"
  37. #include "common/mac/macho_utilities.h"
  38. #include "common/mac/scoped_task_suspend-inl.h"
  39. #include "google_breakpad/common/minidump_exception_mac.h"
  40. #ifndef USE_PROTECTED_ALLOCATIONS
  41. #if TARGET_OS_IPHONE
  42. #define USE_PROTECTED_ALLOCATIONS 1
  43. #else
  44. #define USE_PROTECTED_ALLOCATIONS 0
  45. #endif
  46. #endif
  47. // If USE_PROTECTED_ALLOCATIONS is activated then the
  48. // gBreakpadAllocator needs to be setup in other code
  49. // ahead of time. Please see ProtectedMemoryAllocator.h
  50. // for more details.
  51. #if USE_PROTECTED_ALLOCATIONS
  52. #include "protected_memory_allocator.h"
  53. extern ProtectedMemoryAllocator *gBreakpadAllocator;
  54. #endif
  55. namespace google_breakpad {
  56. static union {
  57. #if USE_PROTECTED_ALLOCATIONS
  58. char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
  59. #endif
  60. google_breakpad::ExceptionHandler *handler;
  61. } gProtectedData;
  62. using std::map;
  63. // These structures and techniques are illustrated in
  64. // Mac OS X Internals, Amit Singh, ch 9.7
  65. struct ExceptionMessage {
  66. mach_msg_header_t header;
  67. mach_msg_body_t body;
  68. mach_msg_port_descriptor_t thread;
  69. mach_msg_port_descriptor_t task;
  70. NDR_record_t ndr;
  71. exception_type_t exception;
  72. mach_msg_type_number_t code_count;
  73. integer_t code[EXCEPTION_CODE_MAX];
  74. char padding[512];
  75. };
  76. struct ExceptionParameters {
  77. ExceptionParameters() : count(0) {}
  78. mach_msg_type_number_t count;
  79. exception_mask_t masks[EXC_TYPES_COUNT];
  80. mach_port_t ports[EXC_TYPES_COUNT];
  81. exception_behavior_t behaviors[EXC_TYPES_COUNT];
  82. thread_state_flavor_t flavors[EXC_TYPES_COUNT];
  83. };
  84. struct ExceptionReplyMessage {
  85. mach_msg_header_t header;
  86. NDR_record_t ndr;
  87. kern_return_t return_code;
  88. };
  89. // Only catch these three exceptions. The other ones are nebulously defined
  90. // and may result in treating a non-fatal exception as fatal.
  91. exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
  92. EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
  93. #if !TARGET_OS_IPHONE
  94. extern "C"
  95. {
  96. // Forward declarations for functions that need "C" style compilation
  97. boolean_t exc_server(mach_msg_header_t *request,
  98. mach_msg_header_t *reply);
  99. // This symbol must be visible to dlsym() - see
  100. // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
  101. kern_return_t catch_exception_raise(mach_port_t target_port,
  102. mach_port_t failed_thread,
  103. mach_port_t task,
  104. exception_type_t exception,
  105. exception_data_t code,
  106. mach_msg_type_number_t code_count)
  107. __attribute__((visibility("default")));
  108. }
  109. #endif
  110. kern_return_t ForwardException(mach_port_t task,
  111. mach_port_t failed_thread,
  112. exception_type_t exception,
  113. exception_data_t code,
  114. mach_msg_type_number_t code_count);
  115. #if TARGET_OS_IPHONE
  116. // Implementation is based on the implementation generated by mig.
  117. boolean_t breakpad_exc_server(mach_msg_header_t *InHeadP,
  118. mach_msg_header_t *OutHeadP) {
  119. OutHeadP->msgh_bits =
  120. MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0);
  121. OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port;
  122. /* Minimal size: routine() will update it if different */
  123. OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
  124. OutHeadP->msgh_local_port = MACH_PORT_NULL;
  125. OutHeadP->msgh_id = InHeadP->msgh_id + 100;
  126. if (InHeadP->msgh_id != 2401) {
  127. ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
  128. ((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
  129. return FALSE;
  130. }
  131. #ifdef __MigPackStructs
  132. #pragma pack(4)
  133. #endif
  134. typedef struct {
  135. mach_msg_header_t Head;
  136. /* start of the kernel processed data */
  137. mach_msg_body_t msgh_body;
  138. mach_msg_port_descriptor_t thread;
  139. mach_msg_port_descriptor_t task;
  140. /* end of the kernel processed data */
  141. NDR_record_t NDR;
  142. exception_type_t exception;
  143. mach_msg_type_number_t codeCnt;
  144. integer_t code[2];
  145. mach_msg_trailer_t trailer;
  146. } Request;
  147. typedef struct {
  148. mach_msg_header_t Head;
  149. NDR_record_t NDR;
  150. kern_return_t RetCode;
  151. } Reply;
  152. #ifdef __MigPackStructs
  153. #pragma pack()
  154. #endif
  155. Request *In0P = (Request *)InHeadP;
  156. Reply *OutP = (Reply *)OutHeadP;
  157. if (In0P->task.name != mach_task_self()) {
  158. return FALSE;
  159. }
  160. OutP->RetCode = ForwardException(In0P->task.name,
  161. In0P->thread.name,
  162. In0P->exception,
  163. In0P->code,
  164. In0P->codeCnt);
  165. OutP->NDR = NDR_record;
  166. return TRUE;
  167. }
  168. #else
  169. boolean_t breakpad_exc_server(mach_msg_header_t *request,
  170. mach_msg_header_t *reply) {
  171. return exc_server(request, reply);
  172. }
  173. // Callback from exc_server()
  174. kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
  175. mach_port_t task,
  176. exception_type_t exception,
  177. exception_data_t code,
  178. mach_msg_type_number_t code_count) {
  179. if (task != mach_task_self()) {
  180. return KERN_FAILURE;
  181. }
  182. return ForwardException(task, failed_thread, exception, code, code_count);
  183. }
  184. #endif
  185. ExceptionHandler::ExceptionHandler(const string &dump_path,
  186. FilterCallback filter,
  187. MinidumpCallback callback,
  188. void *callback_context,
  189. bool install_handler,
  190. const char *port_name)
  191. : dump_path_(),
  192. filter_(filter),
  193. callback_(callback),
  194. callback_context_(callback_context),
  195. directCallback_(NULL),
  196. handler_thread_(NULL),
  197. handler_port_(MACH_PORT_NULL),
  198. previous_(NULL),
  199. installed_exception_handler_(false),
  200. is_in_teardown_(false),
  201. last_minidump_write_result_(false),
  202. use_minidump_write_mutex_(false) {
  203. // This will update to the ID and C-string pointers
  204. set_dump_path(dump_path);
  205. MinidumpGenerator::GatherSystemInformation();
  206. #if !TARGET_OS_IPHONE
  207. if (port_name)
  208. crash_generation_client_.reset(new CrashGenerationClient(port_name));
  209. #endif
  210. Setup(install_handler);
  211. }
  212. // special constructor if we want to bypass minidump writing and
  213. // simply get a callback with the exception information
  214. ExceptionHandler::ExceptionHandler(DirectCallback callback,
  215. void *callback_context,
  216. bool install_handler)
  217. : dump_path_(),
  218. filter_(NULL),
  219. callback_(NULL),
  220. callback_context_(callback_context),
  221. directCallback_(callback),
  222. handler_thread_(NULL),
  223. handler_port_(MACH_PORT_NULL),
  224. previous_(NULL),
  225. installed_exception_handler_(false),
  226. is_in_teardown_(false),
  227. last_minidump_write_result_(false),
  228. use_minidump_write_mutex_(false) {
  229. MinidumpGenerator::GatherSystemInformation();
  230. Setup(install_handler);
  231. }
  232. ExceptionHandler::~ExceptionHandler() {
  233. Teardown();
  234. }
  235. bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
  236. // If we're currently writing, just return
  237. if (use_minidump_write_mutex_)
  238. return false;
  239. use_minidump_write_mutex_ = true;
  240. last_minidump_write_result_ = false;
  241. // Lock the mutex. Since we just created it, this will return immediately.
  242. if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
  243. // Send an empty message to the handle port so that a minidump will
  244. // be written
  245. SendMessageToHandlerThread(write_exception_stream ?
  246. kWriteDumpWithExceptionMessage :
  247. kWriteDumpMessage);
  248. // Wait for the minidump writer to complete its writing. It will unlock
  249. // the mutex when completed
  250. pthread_mutex_lock(&minidump_write_mutex_);
  251. }
  252. use_minidump_write_mutex_ = false;
  253. UpdateNextID();
  254. return last_minidump_write_result_;
  255. }
  256. // static
  257. bool ExceptionHandler::WriteMinidump(const string &dump_path,
  258. bool write_exception_stream,
  259. MinidumpCallback callback,
  260. void *callback_context) {
  261. ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
  262. NULL);
  263. return handler.WriteMinidump(write_exception_stream);
  264. }
  265. // static
  266. bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
  267. mach_port_t child_blamed_thread,
  268. const string &dump_path,
  269. MinidumpCallback callback,
  270. void *callback_context) {
  271. ScopedTaskSuspend suspend(child);
  272. MinidumpGenerator generator(child, MACH_PORT_NULL);
  273. string dump_id;
  274. string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
  275. generator.SetExceptionInformation(EXC_BREAKPOINT,
  276. #if defined (__i386__) || defined(__x86_64__)
  277. EXC_I386_BPT,
  278. #elif defined (__ppc__) || defined (__ppc64__)
  279. EXC_PPC_BREAKPOINT,
  280. #elif defined (__arm__)
  281. EXC_ARM_BREAKPOINT,
  282. #else
  283. #error architecture not supported
  284. #endif
  285. 0,
  286. child_blamed_thread);
  287. bool result = generator.Write(dump_filename.c_str());
  288. if (callback) {
  289. return callback(dump_path.c_str(), dump_id.c_str(),
  290. callback_context, result);
  291. }
  292. return result;
  293. }
  294. bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
  295. int exception_code,
  296. int exception_subcode,
  297. mach_port_t thread_name,
  298. bool exit_after_write,
  299. bool report_current_thread) {
  300. bool result = false;
  301. if (directCallback_) {
  302. if (directCallback_(callback_context_,
  303. exception_type,
  304. exception_code,
  305. exception_subcode,
  306. thread_name) ) {
  307. if (exit_after_write)
  308. _exit(exception_type);
  309. }
  310. #if !TARGET_OS_IPHONE
  311. } else if (IsOutOfProcess()) {
  312. if (exception_type && exception_code) {
  313. // If this is a real exception, give the filter (if any) a chance to
  314. // decide if this should be sent.
  315. if (filter_ && !filter_(callback_context_))
  316. return false;
  317. return crash_generation_client_->RequestDumpForException(
  318. exception_type,
  319. exception_code,
  320. exception_subcode,
  321. thread_name);
  322. }
  323. #endif
  324. } else {
  325. string minidump_id;
  326. // Putting the MinidumpGenerator in its own context will ensure that the
  327. // destructor is executed, closing the newly created minidump file.
  328. if (!dump_path_.empty()) {
  329. MinidumpGenerator md(mach_task_self(),
  330. report_current_thread ? MACH_PORT_NULL :
  331. mach_thread_self());
  332. if (exception_type && exception_code) {
  333. // If this is a real exception, give the filter (if any) a chance to
  334. // decide if this should be sent.
  335. if (filter_ && !filter_(callback_context_))
  336. return false;
  337. md.SetExceptionInformation(exception_type, exception_code,
  338. exception_subcode, thread_name);
  339. }
  340. result = md.Write(next_minidump_path_c_);
  341. }
  342. // Call user specified callback (if any)
  343. if (callback_) {
  344. // If the user callback returned true and we're handling an exception
  345. // (rather than just writing out the file), then we should exit without
  346. // forwarding the exception to the next handler.
  347. if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
  348. result)) {
  349. if (exit_after_write)
  350. _exit(exception_type);
  351. }
  352. }
  353. }
  354. return result;
  355. }
  356. kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
  357. exception_type_t exception,
  358. exception_data_t code,
  359. mach_msg_type_number_t code_count) {
  360. // At this time, we should have called Uninstall() on the exception handler
  361. // so that the current exception ports are the ones that we should be
  362. // forwarding to.
  363. ExceptionParameters current;
  364. current.count = EXC_TYPES_COUNT;
  365. mach_port_t current_task = mach_task_self();
  366. task_get_exception_ports(current_task,
  367. s_exception_mask,
  368. current.masks,
  369. &current.count,
  370. current.ports,
  371. current.behaviors,
  372. current.flavors);
  373. // Find the first exception handler that matches the exception
  374. unsigned int found;
  375. for (found = 0; found < current.count; ++found) {
  376. if (current.masks[found] & (1 << exception)) {
  377. break;
  378. }
  379. }
  380. // Nothing to forward
  381. if (found == current.count) {
  382. fprintf(stderr, "** No previous ports for forwarding!! \n");
  383. exit(KERN_FAILURE);
  384. }
  385. mach_port_t target_port = current.ports[found];
  386. exception_behavior_t target_behavior = current.behaviors[found];
  387. kern_return_t result;
  388. switch (target_behavior) {
  389. case EXCEPTION_DEFAULT:
  390. result = exception_raise(target_port, failed_thread, task, exception,
  391. code, code_count);
  392. break;
  393. default:
  394. fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior);
  395. result = KERN_FAILURE;
  396. break;
  397. }
  398. return result;
  399. }
  400. // static
  401. void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
  402. ExceptionHandler *self =
  403. reinterpret_cast<ExceptionHandler *>(exception_handler_class);
  404. ExceptionMessage receive;
  405. // Wait for the exception info
  406. while (1) {
  407. receive.header.msgh_local_port = self->handler_port_;
  408. receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
  409. kern_return_t result = mach_msg(&(receive.header),
  410. MACH_RCV_MSG | MACH_RCV_LARGE, 0,
  411. receive.header.msgh_size,
  412. self->handler_port_,
  413. MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  414. if (result == KERN_SUCCESS) {
  415. // Uninstall our handler so that we don't get in a loop if the process of
  416. // writing out a minidump causes an exception. However, if the exception
  417. // was caused by a fork'd process, don't uninstall things
  418. // If the actual exception code is zero, then we're calling this handler
  419. // in a way that indicates that we want to either exit this thread or
  420. // generate a minidump
  421. //
  422. // While reporting, all threads (except this one) must be suspended
  423. // to avoid misleading stacks. If appropriate they will be resumed
  424. // afterwards.
  425. if (!receive.exception) {
  426. // Don't touch self, since this message could have been sent
  427. // from its destructor.
  428. if (receive.header.msgh_id == kShutdownMessage)
  429. return NULL;
  430. self->SuspendThreads();
  431. #if USE_PROTECTED_ALLOCATIONS
  432. if (gBreakpadAllocator)
  433. gBreakpadAllocator->Unprotect();
  434. #endif
  435. mach_port_t thread = MACH_PORT_NULL;
  436. int exception_type = 0;
  437. int exception_code = 0;
  438. if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
  439. thread = receive.thread.name;
  440. exception_type = EXC_BREAKPOINT;
  441. #if defined (__i386__) || defined(__x86_64__)
  442. exception_code = EXC_I386_BPT;
  443. #elif defined (__ppc__) || defined (__ppc64__)
  444. exception_code = EXC_PPC_BREAKPOINT;
  445. #elif defined (__arm__)
  446. exception_code = EXC_ARM_BREAKPOINT;
  447. #else
  448. #error architecture not supported
  449. #endif
  450. }
  451. // Write out the dump and save the result for later retrieval
  452. self->last_minidump_write_result_ =
  453. self->WriteMinidumpWithException(exception_type, exception_code,
  454. 0, thread,
  455. false, false);
  456. #if USE_PROTECTED_ALLOCATIONS
  457. if (gBreakpadAllocator)
  458. gBreakpadAllocator->Protect();
  459. #endif
  460. self->ResumeThreads();
  461. if (self->use_minidump_write_mutex_)
  462. pthread_mutex_unlock(&self->minidump_write_mutex_);
  463. } else {
  464. // When forking a child process with the exception handler installed,
  465. // if the child crashes, it will send the exception back to the parent
  466. // process. The check for task == self_task() ensures that only
  467. // exceptions that occur in the parent process are caught and
  468. // processed. If the exception was not caused by this task, we
  469. // still need to call into the exception server and have it return
  470. // KERN_FAILURE (see catch_exception_raise) in order for the kernel
  471. // to move onto the host exception handler for the child task
  472. if (receive.task.name == mach_task_self()) {
  473. self->SuspendThreads();
  474. #if USE_PROTECTED_ALLOCATIONS
  475. if (gBreakpadAllocator)
  476. gBreakpadAllocator->Unprotect();
  477. #endif
  478. int subcode = 0;
  479. if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
  480. subcode = receive.code[1];
  481. // Generate the minidump with the exception data.
  482. self->WriteMinidumpWithException(receive.exception, receive.code[0],
  483. subcode, receive.thread.name, true,
  484. false);
  485. #if USE_PROTECTED_ALLOCATIONS
  486. // This may have become protected again within
  487. // WriteMinidumpWithException, but it needs to be unprotected for
  488. // UninstallHandler.
  489. if (gBreakpadAllocator)
  490. gBreakpadAllocator->Unprotect();
  491. #endif
  492. self->UninstallHandler(true);
  493. #if USE_PROTECTED_ALLOCATIONS
  494. if (gBreakpadAllocator)
  495. gBreakpadAllocator->Protect();
  496. #endif
  497. }
  498. // Pass along the exception to the server, which will setup the
  499. // message and call catch_exception_raise() and put the return
  500. // code into the reply.
  501. ExceptionReplyMessage reply;
  502. if (!breakpad_exc_server(&receive.header, &reply.header))
  503. exit(1);
  504. // Send a reply and exit
  505. mach_msg(&(reply.header), MACH_SEND_MSG,
  506. reply.header.msgh_size, 0, MACH_PORT_NULL,
  507. MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  508. }
  509. }
  510. }
  511. return NULL;
  512. }
  513. //static
  514. void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
  515. #if USE_PROTECTED_ALLOCATIONS
  516. if (gBreakpadAllocator)
  517. gBreakpadAllocator->Unprotect();
  518. #endif
  519. gProtectedData.handler->WriteMinidumpWithException(
  520. EXC_SOFTWARE,
  521. MD_EXCEPTION_CODE_MAC_ABORT,
  522. 0,
  523. mach_thread_self(),
  524. true,
  525. true);
  526. #if USE_PROTECTED_ALLOCATIONS
  527. if (gBreakpadAllocator)
  528. gBreakpadAllocator->Protect();
  529. #endif
  530. }
  531. bool ExceptionHandler::InstallHandler() {
  532. // If a handler is already installed, something is really wrong.
  533. if (gProtectedData.handler != NULL) {
  534. return false;
  535. }
  536. #if TARGET_OS_IPHONE
  537. if (!IsOutOfProcess()) {
  538. struct sigaction sa;
  539. memset(&sa, 0, sizeof(sa));
  540. sigemptyset(&sa.sa_mask);
  541. sigaddset(&sa.sa_mask, SIGABRT);
  542. sa.sa_sigaction = ExceptionHandler::SignalHandler;
  543. sa.sa_flags = SA_SIGINFO;
  544. scoped_ptr<struct sigaction> old(new struct sigaction);
  545. if (sigaction(SIGABRT, &sa, old.get()) == -1) {
  546. return false;
  547. }
  548. old_handler_.swap(old);
  549. gProtectedData.handler = this;
  550. #if USE_PROTECTED_ALLOCATIONS
  551. assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
  552. mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
  553. #endif
  554. }
  555. #endif
  556. try {
  557. #if USE_PROTECTED_ALLOCATIONS
  558. previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
  559. ExceptionParameters();
  560. #else
  561. previous_ = new ExceptionParameters();
  562. #endif
  563. }
  564. catch (std::bad_alloc) {
  565. return false;
  566. }
  567. // Save the current exception ports so that we can forward to them
  568. previous_->count = EXC_TYPES_COUNT;
  569. mach_port_t current_task = mach_task_self();
  570. kern_return_t result = task_get_exception_ports(current_task,
  571. s_exception_mask,
  572. previous_->masks,
  573. &previous_->count,
  574. previous_->ports,
  575. previous_->behaviors,
  576. previous_->flavors);
  577. // Setup the exception ports on this task
  578. if (result == KERN_SUCCESS)
  579. result = task_set_exception_ports(current_task, s_exception_mask,
  580. handler_port_, EXCEPTION_DEFAULT,
  581. THREAD_STATE_NONE);
  582. installed_exception_handler_ = (result == KERN_SUCCESS);
  583. return installed_exception_handler_;
  584. }
  585. bool ExceptionHandler::UninstallHandler(bool in_exception) {
  586. kern_return_t result = KERN_SUCCESS;
  587. if (old_handler_.get()) {
  588. sigaction(SIGABRT, old_handler_.get(), NULL);
  589. #if USE_PROTECTED_ALLOCATIONS
  590. mprotect(gProtectedData.protected_buffer, PAGE_SIZE,
  591. PROT_READ | PROT_WRITE);
  592. #endif
  593. old_handler_.reset();
  594. gProtectedData.handler = NULL;
  595. }
  596. if (installed_exception_handler_) {
  597. mach_port_t current_task = mach_task_self();
  598. // Restore the previous ports
  599. for (unsigned int i = 0; i < previous_->count; ++i) {
  600. result = task_set_exception_ports(current_task, previous_->masks[i],
  601. previous_->ports[i],
  602. previous_->behaviors[i],
  603. previous_->flavors[i]);
  604. if (result != KERN_SUCCESS)
  605. return false;
  606. }
  607. // this delete should NOT happen if an exception just occurred!
  608. if (!in_exception) {
  609. #if USE_PROTECTED_ALLOCATIONS
  610. previous_->~ExceptionParameters();
  611. #else
  612. delete previous_;
  613. #endif
  614. }
  615. previous_ = NULL;
  616. installed_exception_handler_ = false;
  617. }
  618. return result == KERN_SUCCESS;
  619. }
  620. bool ExceptionHandler::Setup(bool install_handler) {
  621. if (pthread_mutex_init(&minidump_write_mutex_, NULL))
  622. return false;
  623. // Create a receive right
  624. mach_port_t current_task = mach_task_self();
  625. kern_return_t result = mach_port_allocate(current_task,
  626. MACH_PORT_RIGHT_RECEIVE,
  627. &handler_port_);
  628. // Add send right
  629. if (result == KERN_SUCCESS)
  630. result = mach_port_insert_right(current_task, handler_port_, handler_port_,
  631. MACH_MSG_TYPE_MAKE_SEND);
  632. if (install_handler && result == KERN_SUCCESS)
  633. if (!InstallHandler())
  634. return false;
  635. if (result == KERN_SUCCESS) {
  636. // Install the handler in its own thread, detached as we won't be joining.
  637. pthread_attr_t attr;
  638. pthread_attr_init(&attr);
  639. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  640. int thread_create_result = pthread_create(&handler_thread_, &attr,
  641. &WaitForMessage, this);
  642. pthread_attr_destroy(&attr);
  643. result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
  644. }
  645. return result == KERN_SUCCESS ? true : false;
  646. }
  647. bool ExceptionHandler::Teardown() {
  648. kern_return_t result = KERN_SUCCESS;
  649. is_in_teardown_ = true;
  650. if (!UninstallHandler(false))
  651. return false;
  652. // Send an empty message so that the handler_thread exits
  653. if (SendMessageToHandlerThread(kShutdownMessage)) {
  654. mach_port_t current_task = mach_task_self();
  655. result = mach_port_deallocate(current_task, handler_port_);
  656. if (result != KERN_SUCCESS)
  657. return false;
  658. } else {
  659. return false;
  660. }
  661. handler_thread_ = NULL;
  662. handler_port_ = MACH_PORT_NULL;
  663. pthread_mutex_destroy(&minidump_write_mutex_);
  664. return result == KERN_SUCCESS;
  665. }
  666. bool ExceptionHandler::SendMessageToHandlerThread(
  667. HandlerThreadMessage message_id) {
  668. ExceptionMessage msg;
  669. memset(&msg, 0, sizeof(msg));
  670. msg.header.msgh_id = message_id;
  671. if (message_id == kWriteDumpMessage ||
  672. message_id == kWriteDumpWithExceptionMessage) {
  673. // Include this thread's port.
  674. msg.thread.name = mach_thread_self();
  675. msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
  676. msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
  677. }
  678. msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
  679. msg.header.msgh_remote_port = handler_port_;
  680. msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
  681. MACH_MSG_TYPE_MAKE_SEND_ONCE);
  682. kern_return_t result = mach_msg(&(msg.header),
  683. MACH_SEND_MSG | MACH_SEND_TIMEOUT,
  684. msg.header.msgh_size, 0, 0,
  685. MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  686. return result == KERN_SUCCESS;
  687. }
  688. void ExceptionHandler::UpdateNextID() {
  689. next_minidump_path_ =
  690. (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
  691. next_minidump_path_c_ = next_minidump_path_.c_str();
  692. next_minidump_id_c_ = next_minidump_id_.c_str();
  693. }
  694. bool ExceptionHandler::SuspendThreads() {
  695. thread_act_port_array_t threads_for_task;
  696. mach_msg_type_number_t thread_count;
  697. if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
  698. return false;
  699. // suspend all of the threads except for this one
  700. for (unsigned int i = 0; i < thread_count; ++i) {
  701. if (threads_for_task[i] != mach_thread_self()) {
  702. if (thread_suspend(threads_for_task[i]))
  703. return false;
  704. }
  705. }
  706. return true;
  707. }
  708. bool ExceptionHandler::ResumeThreads() {
  709. thread_act_port_array_t threads_for_task;
  710. mach_msg_type_number_t thread_count;
  711. if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
  712. return false;
  713. // resume all of the threads except for this one
  714. for (unsigned int i = 0; i < thread_count; ++i) {
  715. if (threads_for_task[i] != mach_thread_self()) {
  716. if (thread_resume(threads_for_task[i]))
  717. return false;
  718. }
  719. }
  720. return true;
  721. }
  722. } // namespace google_breakpad