Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // For linux_syscall_support.h. This makes it safe to call embedded system | 5 // For linux_syscall_support.h. This makes it safe to call embedded system |
| 6 // calls when in seccomp mode. | 6 // calls when in seccomp mode. |
| 7 #define SYS_SYSCALL_ENTRYPOINT "playground$syscallEntryPoint" | 7 #define SYS_SYSCALL_ENTRYPOINT "playground$syscallEntryPoint" |
| 8 | 8 |
| 9 #include "chrome/app/breakpad_linux.h" | 9 #include "chrome/app/breakpad_linux.h" |
| 10 | 10 |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 86 const char kUploadURL[] = "https://clients2.google.com/cr/staging_report"; | 86 const char kUploadURL[] = "https://clients2.google.com/cr/staging_report"; |
| 87 #endif | 87 #endif |
| 88 | 88 |
| 89 bool g_is_crash_reporter_enabled = false; | 89 bool g_is_crash_reporter_enabled = false; |
| 90 uint64_t g_process_start_time = 0; | 90 uint64_t g_process_start_time = 0; |
| 91 char* g_crash_log_path = NULL; | 91 char* g_crash_log_path = NULL; |
| 92 ExceptionHandler* g_breakpad = NULL; | 92 ExceptionHandler* g_breakpad = NULL; |
| 93 #if defined(ADDRESS_SANITIZER) | 93 #if defined(ADDRESS_SANITIZER) |
| 94 const char* g_asan_report_str = NULL; | 94 const char* g_asan_report_str = NULL; |
| 95 #endif | 95 #endif |
| 96 #if defined(OS_ANDROID) | |
| 97 char* g_process_type = NULL; | |
| 98 #endif | |
| 96 | 99 |
| 97 // Writes the value |v| as 16 hex characters to the memory pointed at by | 100 // Writes the value |v| as 16 hex characters to the memory pointed at by |
| 98 // |output|. | 101 // |output|. |
| 99 void write_uint64_hex(char* output, uint64_t v) { | 102 void write_uint64_hex(char* output, uint64_t v) { |
| 100 static const char hextable[] = "0123456789abcdef"; | 103 static const char hextable[] = "0123456789abcdef"; |
| 101 | 104 |
| 102 for (int i = 15; i >= 0; --i) { | 105 for (int i = 15; i >= 0; --i) { |
| 103 output[i] = hextable[v & 15]; | 106 output[i] = hextable[v & 15]; |
| 104 v >>= 4; | 107 v >>= 4; |
| 105 } | 108 } |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 119 uint64_t kernel_timeval_to_ms(struct kernel_timeval *tv) { | 122 uint64_t kernel_timeval_to_ms(struct kernel_timeval *tv) { |
| 120 uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t. | 123 uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t. |
| 121 ret *= 1000; | 124 ret *= 1000; |
| 122 ret += tv->tv_usec / 1000; | 125 ret += tv->tv_usec / 1000; |
| 123 return ret; | 126 return ret; |
| 124 } | 127 } |
| 125 | 128 |
| 126 // String buffer size to use to convert a uint64_t to string. | 129 // String buffer size to use to convert a uint64_t to string. |
| 127 size_t kUint64StringSize = 21; | 130 size_t kUint64StringSize = 21; |
| 128 | 131 |
| 132 static void SetProcessStartTime() { | |
| 133 // Set the base process start time value. | |
| 134 struct timeval tv; | |
| 135 if (!gettimeofday(&tv, NULL)) | |
| 136 g_process_start_time = timeval_to_ms(&tv); | |
| 137 else | |
| 138 g_process_start_time = 0; | |
| 139 } | |
| 140 | |
| 129 // uint64_t version of my_int_len() from | 141 // uint64_t version of my_int_len() from |
| 130 // breakpad/src/common/linux/linux_libc_support.h. Return the length of the | 142 // breakpad/src/common/linux/linux_libc_support.h. Return the length of the |
| 131 // given, non-negative integer when expressed in base 10. | 143 // given, non-negative integer when expressed in base 10. |
| 132 unsigned my_uint64_len(uint64_t i) { | 144 unsigned my_uint64_len(uint64_t i) { |
| 133 if (!i) | 145 if (!i) |
| 134 return 1; | 146 return 1; |
| 135 | 147 |
| 136 unsigned len = 0; | 148 unsigned len = 0; |
| 137 while (i) { | 149 while (i) { |
| 138 len++; | 150 len++; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 172 while (*dest) | 184 while (*dest) |
| 173 dest++; | 185 dest++; |
| 174 while (len--) | 186 while (len--) |
| 175 if (!(*dest++ = *src++)) | 187 if (!(*dest++ = *src++)) |
| 176 return ret; | 188 return ret; |
| 177 *dest = 0; | 189 *dest = 0; |
| 178 return ret; | 190 return ret; |
| 179 } | 191 } |
| 180 #endif | 192 #endif |
| 181 | 193 |
| 194 // Populates the passed in allocated strings and their sizes with the GUID, | |
| 195 // crash url and distro of the crashing process. | |
| 196 // The passed strings are expected to be at least kGuidSize, kMaxActiveURLSize | |
| 197 // and kDistroSize bytes long respectively. | |
| 198 void PopulateGUIDAndURLAndDistro(char* guid, size_t* guid_len_param, | |
| 199 char* crash_url, size_t* crash_url_len_param, | |
| 200 char* distro, size_t* distro_len_param) { | |
| 201 size_t guid_len = std::min(my_strlen(child_process_logging::g_client_id), | |
| 202 kGuidSize); | |
| 203 size_t crash_url_len = | |
| 204 std::min(my_strlen(child_process_logging::g_active_url), | |
| 205 kMaxActiveURLSize); | |
| 206 size_t distro_len = std::min(my_strlen(base::g_linux_distro), kDistroSize); | |
| 207 memcpy(guid, child_process_logging::g_client_id, guid_len); | |
| 208 memcpy(crash_url, child_process_logging::g_active_url, crash_url_len); | |
| 209 memcpy(distro, base::g_linux_distro, distro_len); | |
| 210 if (guid_len_param) | |
| 211 *guid_len_param = guid_len; | |
| 212 if (crash_url_len_param) | |
| 213 *crash_url_len_param = crash_url_len; | |
| 214 if (distro_len_param) | |
| 215 *distro_len_param = distro_len; | |
| 216 } | |
| 217 | |
| 182 // MIME substrings. | 218 // MIME substrings. |
| 183 const char g_rn[] = "\r\n"; | 219 const char g_rn[] = "\r\n"; |
| 184 const char g_form_data_msg[] = "Content-Disposition: form-data; name=\""; | 220 const char g_form_data_msg[] = "Content-Disposition: form-data; name=\""; |
| 185 const char g_quote_msg[] = "\""; | 221 const char g_quote_msg[] = "\""; |
| 186 const char g_dashdash_msg[] = "--"; | 222 const char g_dashdash_msg[] = "--"; |
| 187 const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\""; | 223 const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\""; |
| 188 #if defined(ADDRESS_SANITIZER) | 224 #if defined(ADDRESS_SANITIZER) |
| 189 const char g_log_msg[] = "upload_file_log\"; filename=\"log\""; | 225 const char g_log_msg[] = "upload_file_log\"; filename=\"log\""; |
| 190 #endif | 226 #endif |
| 191 const char g_content_type_msg[] = "Content-Type: application/octet-stream"; | 227 const char g_content_type_msg[] = "Content-Type: application/octet-stream"; |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 407 bool succeeded) { | 443 bool succeeded) { |
| 408 return FinalizeCrashDoneAndroid(); | 444 return FinalizeCrashDoneAndroid(); |
| 409 } | 445 } |
| 410 #endif | 446 #endif |
| 411 | 447 |
| 412 bool CrashDone(const MinidumpDescriptor& minidump, | 448 bool CrashDone(const MinidumpDescriptor& minidump, |
| 413 const bool upload, | 449 const bool upload, |
| 414 const bool succeeded) { | 450 const bool succeeded) { |
| 415 // WARNING: this code runs in a compromised context. It may not call into | 451 // WARNING: this code runs in a compromised context. It may not call into |
| 416 // libc nor allocate memory normally. | 452 // libc nor allocate memory normally. |
| 417 if (!succeeded) | 453 if (!succeeded) { |
| 454 const char msg[] = "Failed to generate minidump."; | |
| 455 WriteLog(msg, sizeof(msg) - 1); | |
| 418 return false; | 456 return false; |
| 457 } | |
| 419 | 458 |
| 420 DCHECK(!minidump.IsFD()); | 459 DCHECK(!minidump.IsFD()); |
| 421 | 460 |
| 422 BreakpadInfo info; | 461 BreakpadInfo info = {0}; |
| 423 info.filename = minidump.path(); | 462 info.filename = minidump.path(); |
| 463 info.fd = minidump.fd(); | |
| 424 #if defined(ADDRESS_SANITIZER) | 464 #if defined(ADDRESS_SANITIZER) |
| 425 google_breakpad::PageAllocator allocator; | 465 google_breakpad::PageAllocator allocator; |
| 426 const size_t log_path_len = my_strlen(minidump.path()); | 466 const size_t log_path_len = my_strlen(minidump.path()); |
| 427 char* log_path = reinterpret_cast<char*>(allocator.Alloc(log_path_len + 1)); | 467 char* log_path = reinterpret_cast<char*>(allocator.Alloc(log_path_len + 1)); |
| 428 my_memcpy(log_path, minidump.path(), log_path_len); | 468 my_memcpy(log_path, minidump.path(), log_path_len); |
| 429 my_memcpy(log_path + log_path_len - 4, ".log", 4); | 469 my_memcpy(log_path + log_path_len - 4, ".log", 4); |
| 430 log_path[log_path_len] = '\0'; | 470 log_path[log_path_len] = '\0'; |
| 431 info.log_filename = log_path; | 471 info.log_filename = log_path; |
| 432 #endif | 472 #endif |
| 433 info.process_type = "browser"; | 473 info.process_type = "browser"; |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 459 | 499 |
| 460 #if !defined(OS_ANDROID) | 500 #if !defined(OS_ANDROID) |
| 461 // Wrapper function, do not add more code here. | 501 // Wrapper function, do not add more code here. |
| 462 bool CrashDoneUpload(const MinidumpDescriptor& minidump, | 502 bool CrashDoneUpload(const MinidumpDescriptor& minidump, |
| 463 void* context, | 503 void* context, |
| 464 bool succeeded) { | 504 bool succeeded) { |
| 465 return CrashDone(minidump, true, succeeded); | 505 return CrashDone(minidump, true, succeeded); |
| 466 } | 506 } |
| 467 #endif | 507 #endif |
| 468 | 508 |
| 509 #if defined(OS_ANDROID) | |
|
Lei Zhang
2012/10/23 21:34:58
can we put this closer to NonBrowserCrashHandler()
Jay Civelli
2012/10/24 00:12:53
Done.
| |
| 510 bool CrashDoneInProcessNoUpload( | |
| 511 const google_breakpad::MinidumpDescriptor& descriptor, | |
| 512 void* context, | |
| 513 const bool succeeded) { | |
| 514 // WARNING: this code runs in a compromised context. It may not call into | |
| 515 // libc nor allocate memory normally. | |
| 516 if (!succeeded) { | |
| 517 static const char msg[] = "Crash dump generation failed.\n"; | |
| 518 WriteLog(msg, sizeof(msg) - 1); | |
| 519 return false; | |
| 520 } | |
| 521 | |
| 522 // Start constructing the message to send to the browser. | |
| 523 char guid[kGuidSize + 1] = {0}; | |
| 524 char crash_url[kMaxActiveURLSize + 1] = {0}; | |
| 525 char distro[kDistroSize + 1] = {0}; | |
| 526 size_t guid_length = 0; | |
| 527 size_t crash_url_length = 0; | |
| 528 size_t distro_length = 0; | |
| 529 PopulateGUIDAndURLAndDistro(guid, &guid_length, crash_url, &crash_url_length, | |
| 530 distro, &distro_length); | |
| 531 BreakpadInfo info = {0}; | |
| 532 info.filename = NULL; | |
| 533 info.fd = descriptor.fd(); | |
| 534 info.process_type = g_process_type; | |
| 535 info.process_type_length = my_strlen(g_process_type); | |
| 536 info.crash_url = crash_url; | |
| 537 info.crash_url_length = crash_url_length; | |
| 538 info.guid = guid; | |
| 539 info.guid_length = guid_length; | |
| 540 info.distro = distro; | |
| 541 info.distro_length = distro_length; | |
| 542 info.upload = false; | |
| 543 info.process_start_time = g_process_start_time; | |
| 544 HandleCrashDump(info); | |
| 545 return true; | |
| 546 } | |
| 547 #endif | |
| 548 | |
| 469 #if defined(ADDRESS_SANITIZER) | 549 #if defined(ADDRESS_SANITIZER) |
| 470 extern "C" | 550 extern "C" |
| 471 void __asan_set_error_report_callback(void (*cb)(const char*)); | 551 void __asan_set_error_report_callback(void (*cb)(const char*)); |
| 472 | 552 |
| 473 extern "C" | 553 extern "C" |
| 474 void AsanLinuxBreakpadCallback(const char* report) { | 554 void AsanLinuxBreakpadCallback(const char* report) { |
| 475 g_asan_report_str = report; | 555 g_asan_report_str = report; |
| 476 // Send minidump here. | 556 // Send minidump here. |
| 477 g_breakpad->SimulateSignalDelivery(SIGKILL); | 557 g_breakpad->SimulateSignalDelivery(SIGKILL); |
| 478 } | 558 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 513 g_breakpad = new ExceptionHandler( | 593 g_breakpad = new ExceptionHandler( |
| 514 MinidumpDescriptor(dumps_path.value()), | 594 MinidumpDescriptor(dumps_path.value()), |
| 515 NULL, | 595 NULL, |
| 516 CrashDoneUpload, | 596 CrashDoneUpload, |
| 517 NULL, | 597 NULL, |
| 518 true, // Install handlers. | 598 true, // Install handlers. |
| 519 -1); // Server file descriptor. -1 for in-process. | 599 -1); // Server file descriptor. -1 for in-process. |
| 520 #endif | 600 #endif |
| 521 } | 601 } |
| 522 | 602 |
| 603 #if !defined(OS_ANDROID) | |
| 523 // Non-Browser = Extension, Gpu, Plugins, Ppapi and Renderer | 604 // Non-Browser = Extension, Gpu, Plugins, Ppapi and Renderer |
| 524 bool NonBrowserCrashHandler(const void* crash_context, | 605 bool NonBrowserCrashHandler(const void* crash_context, |
| 525 size_t crash_context_size, | 606 size_t crash_context_size, |
| 526 void* context) { | 607 void* context) { |
| 527 const int fd = reinterpret_cast<intptr_t>(context); | 608 const int fd = reinterpret_cast<intptr_t>(context); |
| 528 int fds[2] = { -1, -1 }; | 609 int fds[2] = { -1, -1 }; |
| 529 if (sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { | 610 if (sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { |
| 530 static const char msg[] = "Failed to create socket for crash dumping.\n"; | 611 static const char msg[] = "Failed to create socket for crash dumping.\n"; |
| 531 WriteLog(msg, sizeof(msg)-1); | 612 WriteLog(msg, sizeof(msg) - 1); |
| 532 return false; | 613 return false; |
| 533 } | 614 } |
| 534 | 615 |
| 535 // Start constructing the message to send to the browser. | 616 // Start constructing the message to send to the browser. |
| 536 char guid[kGuidSize + 1] = {0}; | 617 char guid[kGuidSize + 1] = {0}; |
| 537 char crash_url[kMaxActiveURLSize + 1] = {0}; | 618 char crash_url[kMaxActiveURLSize + 1] = {0}; |
| 538 char distro[kDistroSize + 1] = {0}; | 619 char distro[kDistroSize + 1] = {0}; |
| 539 const size_t guid_len = | 620 PopulateGUIDAndURLAndDistro(guid, NULL, crash_url, NULL, distro, NULL); |
| 540 std::min(my_strlen(child_process_logging::g_client_id), kGuidSize); | |
| 541 const size_t crash_url_len = | |
| 542 std::min(my_strlen(child_process_logging::g_active_url), | |
| 543 kMaxActiveURLSize); | |
| 544 const size_t distro_len = | |
| 545 std::min(my_strlen(base::g_linux_distro), kDistroSize); | |
| 546 memcpy(guid, child_process_logging::g_client_id, guid_len); | |
| 547 memcpy(crash_url, child_process_logging::g_active_url, crash_url_len); | |
| 548 memcpy(distro, base::g_linux_distro, distro_len); | |
| 549 | 621 |
| 550 char b; // Dummy variable for sys_read below. | 622 char b; // Dummy variable for sys_read below. |
| 551 const char* b_addr = &b; // Get the address of |b| so we can create the | 623 const char* b_addr = &b; // Get the address of |b| so we can create the |
| 552 // expected /proc/[pid]/syscall content in the | 624 // expected /proc/[pid]/syscall content in the |
| 553 // browser to convert namespace tids. | 625 // browser to convert namespace tids. |
| 554 | 626 |
| 555 // The length of the control message: | 627 // The length of the control message: |
| 556 static const unsigned kControlMsgSize = sizeof(fds); | 628 static const unsigned kControlMsgSize = sizeof(fds); |
| 557 static const unsigned kControlMsgSpaceSize = CMSG_SPACE(kControlMsgSize); | 629 static const unsigned kControlMsgSpaceSize = CMSG_SPACE(kControlMsgSize); |
| 558 static const unsigned kControlMsgLenSize = CMSG_LEN(kControlMsgSize); | 630 static const unsigned kControlMsgLenSize = CMSG_LEN(kControlMsgSize); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 596 | 668 |
| 597 struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); | 669 struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); |
| 598 hdr->cmsg_level = SOL_SOCKET; | 670 hdr->cmsg_level = SOL_SOCKET; |
| 599 hdr->cmsg_type = SCM_RIGHTS; | 671 hdr->cmsg_type = SCM_RIGHTS; |
| 600 hdr->cmsg_len = kControlMsgLenSize; | 672 hdr->cmsg_len = kControlMsgLenSize; |
| 601 ((int*) CMSG_DATA(hdr))[0] = fds[0]; | 673 ((int*) CMSG_DATA(hdr))[0] = fds[0]; |
| 602 ((int*) CMSG_DATA(hdr))[1] = fds[1]; | 674 ((int*) CMSG_DATA(hdr))[1] = fds[1]; |
| 603 | 675 |
| 604 if (HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)) < 0) { | 676 if (HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)) < 0) { |
| 605 static const char errmsg[] = "Failed to tell parent about crash.\n"; | 677 static const char errmsg[] = "Failed to tell parent about crash.\n"; |
| 606 WriteLog(errmsg, sizeof(errmsg)-1); | 678 WriteLog(errmsg, sizeof(errmsg) - 1); |
| 607 IGNORE_RET(sys_close(fds[1])); | 679 IGNORE_RET(sys_close(fds[1])); |
| 608 return false; | 680 return false; |
| 609 } | 681 } |
| 610 IGNORE_RET(sys_close(fds[1])); | 682 IGNORE_RET(sys_close(fds[1])); |
| 611 | 683 |
| 612 if (HANDLE_EINTR(sys_read(fds[0], &b, 1)) != 1) { | 684 if (HANDLE_EINTR(sys_read(fds[0], &b, 1)) != 1) { |
| 613 static const char errmsg[] = "Parent failed to complete crash dump.\n"; | 685 static const char errmsg[] = "Parent failed to complete crash dump.\n"; |
| 614 WriteLog(errmsg, sizeof(errmsg)-1); | 686 WriteLog(errmsg, sizeof(errmsg) - 1); |
| 615 } | 687 } |
| 616 | 688 |
| 617 #if defined(OS_ANDROID) | 689 #if defined(OS_ANDROID) |
|
Lei Zhang
2012/10/23 21:34:58
This is now a defined(OS_ANDROID) inside a !define
Jay Civelli
2012/10/24 00:12:53
Removed. Also moved that function next to EnableNo
| |
| 618 // When false is returned, breakpad will continue to its minidump generator | 690 // When false is returned, breakpad will continue to its minidump generator |
| 619 // and then to the HandlerCallback, which, in this case, is | 691 // and then to the HandlerCallback, which, in this case, is |
| 620 // CrashDoneNonBrowserAndroid(). | 692 // CrashDoneNonBrowserAndroid(). |
| 621 return false; | 693 return false; |
| 622 #else | 694 #else |
| 623 return true; | 695 return true; |
| 624 #endif | 696 #endif |
| 625 } | 697 } |
| 698 #endif | |
| 626 | 699 |
| 700 #if defined(OS_ANDROID) | |
| 701 void EnableNonBrowserCrashDumping(int minidump_fd) { | |
| 702 // This will guarantee that the BuildInfo has been initialized and subsequent | |
| 703 // calls will not require memory allocation. | |
| 704 base::android::BuildInfo::GetInstance(); | |
| 705 child_process_logging::SetClientId("Android"); | |
| 706 | |
| 707 // On Android, the current sandboxing uses process isolation, in which the | |
| 708 // child process runs with a different UID. That breaks the normal crash | |
| 709 // reporting where the browser process generates the minidump by inspecting | |
| 710 // the child process. This is because the browser process now does not have | |
| 711 // the permission to access the states of the child process (as it has a | |
| 712 // different UID). | |
| 713 // TODO(jcivelli): http://b/issue?id=6776356 we should use a watchdog | |
| 714 // process forked from the renderer process that generates the minidump. | |
| 715 if (minidump_fd == -1) { | |
| 716 LOG(ERROR) << "Minidump file descriptor not found, crash reporting will " | |
| 717 " not work."; | |
| 718 return; | |
| 719 } | |
| 720 SetProcessStartTime(); | |
| 721 | |
| 722 g_is_crash_reporter_enabled = true; | |
| 723 // Save the process type (it is leaked). | |
| 724 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); | |
| 725 const std::string process_type = | |
| 726 parsed_command_line.GetSwitchValueASCII(switches::kProcessType); | |
| 727 const size_t process_type_len = process_type.size() + 1; | |
| 728 g_process_type = new char[process_type_len]; | |
| 729 strncpy(g_process_type, process_type.c_str(), process_type_len); | |
| 730 new google_breakpad::ExceptionHandler(MinidumpDescriptor(minidump_fd), | |
| 731 NULL, CrashDoneInProcessNoUpload, NULL, true, -1); | |
| 732 } | |
| 733 #else | |
| 627 void EnableNonBrowserCrashDumping() { | 734 void EnableNonBrowserCrashDumping() { |
| 628 const int fd = base::GlobalDescriptors::GetInstance()->Get(kCrashDumpSignal); | 735 const int fd = base::GlobalDescriptors::GetInstance()->Get(kCrashDumpSignal); |
| 629 g_is_crash_reporter_enabled = true; | 736 g_is_crash_reporter_enabled = true; |
| 630 // We deliberately leak this object. | 737 // We deliberately leak this object. |
| 631 DCHECK(!g_breakpad); | 738 DCHECK(!g_breakpad); |
| 632 | 739 |
| 633 ExceptionHandler::MinidumpCallback crash_done_callback = NULL; | 740 ExceptionHandler::MinidumpCallback crash_done_callback = NULL; |
| 634 #if defined(OS_ANDROID) | 741 #if defined(OS_ANDROID) |
| 635 crash_done_callback = CrashDoneNonBrowserAndroid; | 742 crash_done_callback = CrashDoneNonBrowserAndroid; |
| 636 #endif | 743 #endif |
| 637 | 744 |
| 638 g_breakpad = new ExceptionHandler( | 745 g_breakpad = new ExceptionHandler( |
| 639 MinidumpDescriptor("/tmp"), // Unused but needed or Breakpad will assert. | 746 MinidumpDescriptor("/tmp"), // Unused but needed or Breakpad will assert. |
| 640 NULL, | 747 NULL, |
| 641 crash_done_callback, | 748 crash_done_callback, |
| 642 reinterpret_cast<void*>(fd), // Param passed to the crash handler. | 749 reinterpret_cast<void*>(fd), // Param passed to the crash handler. |
| 643 true, | 750 true, |
| 644 -1); | 751 -1); |
| 645 g_breakpad->set_crash_handler(NonBrowserCrashHandler); | 752 g_breakpad->set_crash_handler(NonBrowserCrashHandler); |
| 646 } | 753 } |
| 754 #endif | |
| 647 | 755 |
| 648 } // namespace | 756 } // namespace |
| 649 | 757 |
| 758 void LoadDataFromFD(google_breakpad::PageAllocator& allocator, | |
| 759 int fd, bool close_fd, uint8_t** file_data, size_t* size) { | |
| 760 STAT_STRUCT st; | |
| 761 if (FSTAT_FUNC(fd, &st) != 0) { | |
| 762 static const char msg[] = "Cannot upload crash dump: stat failed\n"; | |
| 763 WriteLog(msg, sizeof(msg) - 1); | |
| 764 if (close_fd) | |
| 765 IGNORE_RET(sys_close(fd)); | |
| 766 return; | |
| 767 } | |
| 768 | |
| 769 *file_data = reinterpret_cast<uint8_t*>(allocator.Alloc(st.st_size)); | |
| 770 if (!(*file_data)) { | |
| 771 static const char msg[] = "Cannot upload crash dump: cannot alloc\n"; | |
| 772 WriteLog(msg, sizeof(msg) - 1); | |
| 773 if (close_fd) | |
| 774 IGNORE_RET(sys_close(fd)); | |
| 775 return; | |
| 776 } | |
| 777 my_memset(*file_data, 0xf, st.st_size); | |
| 778 | |
| 779 *size = st.st_size; | |
| 780 int byte_read = sys_read(fd, *file_data, *size); | |
| 781 if (byte_read == -1) { | |
| 782 static const char msg[] = "Cannot upload crash dump: read failed\n"; | |
| 783 WriteLog(msg, sizeof(msg) - 1); | |
| 784 if (close_fd) | |
| 785 IGNORE_RET(sys_close(fd)); | |
| 786 return; | |
| 787 } | |
| 788 | |
| 789 if (close_fd) | |
| 790 IGNORE_RET(sys_close(fd)); | |
| 791 } | |
| 792 | |
| 650 void LoadDataFromFile(google_breakpad::PageAllocator& allocator, | 793 void LoadDataFromFile(google_breakpad::PageAllocator& allocator, |
| 651 const BreakpadInfo& info, const char* filename, | 794 const char* filename, |
| 652 int* fd, uint8_t** file_data, size_t* size) { | 795 int* fd, uint8_t** file_data, size_t* size) { |
| 653 // WARNING: this code runs in a compromised context. It may not call into | 796 // WARNING: this code runs in a compromised context. It may not call into |
| 654 // libc nor allocate memory normally. | 797 // libc nor allocate memory normally. |
| 655 *fd = sys_open(filename, O_RDONLY, 0); | 798 *fd = sys_open(filename, O_RDONLY, 0); |
| 656 *size = 0; | 799 *size = 0; |
| 657 | 800 |
| 658 if (*fd < 0) { | 801 if (*fd < 0) { |
| 659 static const char msg[] = "Cannot upload crash dump: failed to open\n"; | 802 static const char msg[] = "Cannot upload crash dump: failed to open\n"; |
| 660 WriteLog(msg, sizeof(msg)); | 803 WriteLog(msg, sizeof(msg) - 1); |
| 661 return; | |
| 662 } | |
| 663 STAT_STRUCT st; | |
| 664 if (FSTAT_FUNC(*fd, &st) != 0) { | |
| 665 static const char msg[] = "Cannot upload crash dump: stat failed\n"; | |
| 666 WriteLog(msg, sizeof(msg)); | |
| 667 IGNORE_RET(sys_close(*fd)); | |
| 668 return; | 804 return; |
| 669 } | 805 } |
| 670 | 806 |
| 671 *file_data = reinterpret_cast<uint8_t*>(allocator.Alloc(st.st_size)); | 807 LoadDataFromFD(allocator, *fd, true, file_data, size); |
| 672 if (!(*file_data)) { | |
| 673 static const char msg[] = "Cannot upload crash dump: cannot alloc\n"; | |
| 674 WriteLog(msg, sizeof(msg)); | |
| 675 IGNORE_RET(sys_close(*fd)); | |
| 676 return; | |
| 677 } | |
| 678 my_memset(*file_data, 0xf, st.st_size); | |
| 679 | |
| 680 *size = st.st_size; | |
| 681 sys_read(*fd, *file_data, *size); | |
| 682 IGNORE_RET(sys_close(*fd)); | |
| 683 } | 808 } |
| 684 | 809 |
| 685 void HandleCrashDump(const BreakpadInfo& info) { | 810 void HandleCrashDump(const BreakpadInfo& info) { |
| 686 int dumpfd; | 811 int dumpfd; |
| 812 bool keep_fd = false; | |
| 687 size_t dump_size; | 813 size_t dump_size; |
| 688 uint8_t* dump_data; | 814 uint8_t* dump_data; |
| 689 google_breakpad::PageAllocator allocator; | 815 google_breakpad::PageAllocator allocator; |
| 690 LoadDataFromFile(allocator, info, info.filename, | 816 |
| 691 &dumpfd, &dump_data, &dump_size); | 817 if (info.fd != -1) { |
| 818 // Dump is provided with an open FD. | |
| 819 keep_fd = true; | |
| 820 dumpfd = info.fd; | |
| 821 | |
| 822 // The FD is pointing to the end of the file. | |
| 823 // Rewind, we'll read the data next. | |
| 824 if (lseek(dumpfd, 0, SEEK_SET) == -1) { | |
| 825 static const char msg[] = "Cannot upload crash dump: failed to " | |
| 826 "reposition minidump FD\n"; | |
| 827 WriteLog(msg, sizeof(msg) - 1); | |
| 828 IGNORE_RET(sys_close(dumpfd)); | |
| 829 return; | |
| 830 } | |
| 831 LoadDataFromFD(allocator, info.fd, false, &dump_data, &dump_size); | |
| 832 } else { | |
| 833 // Dump is provided with a path. | |
| 834 keep_fd = false; | |
| 835 LoadDataFromFile(allocator, info.filename, &dumpfd, &dump_data, &dump_size); | |
| 836 } | |
| 837 | |
| 838 // TODO(jcivelli): make log work when using FDs. | |
| 692 #if defined(ADDRESS_SANITIZER) | 839 #if defined(ADDRESS_SANITIZER) |
| 693 int logfd; | 840 int logfd; |
| 694 size_t log_size; | 841 size_t log_size; |
| 695 uint8_t* log_data; | 842 uint8_t* log_data; |
| 696 // Load the AddressSanitizer log into log_data. | 843 // Load the AddressSanitizer log into log_data. |
| 697 LoadDataFromFile(allocator, info, info.log_filename, | 844 LoadDataFromFile(allocator, info, info.log_filename, |
| 698 &logfd, &log_data, &log_size); | 845 &logfd, &log_data, &log_size); |
| 699 #endif | 846 #endif |
| 700 | 847 |
| 701 // We need to build a MIME block for uploading to the server. Since we are | 848 // We need to build a MIME block for uploading to the server. Since we are |
| 702 // going to fork and run wget, it needs to be written to a temp file. | 849 // going to fork and run wget, it needs to be written to a temp file. |
| 703 const int ufd = sys_open("/dev/urandom", O_RDONLY, 0); | 850 const int ufd = sys_open("/dev/urandom", O_RDONLY, 0); |
| 704 if (ufd < 0) { | 851 if (ufd < 0) { |
| 705 static const char msg[] = "Cannot upload crash dump because /dev/urandom" | 852 static const char msg[] = "Cannot upload crash dump because /dev/urandom" |
| 706 " is missing\n"; | 853 " is missing\n"; |
| 707 WriteLog(msg, sizeof(msg) - 1); | 854 WriteLog(msg, sizeof(msg) - 1); |
| 708 return; | 855 return; |
| 709 } | 856 } |
| 710 | 857 |
| 711 static const char temp_file_template[] = | 858 static const char temp_file_template[] = |
| 712 "/tmp/chromium-upload-XXXXXXXXXXXXXXXX"; | 859 "/tmp/chromium-upload-XXXXXXXXXXXXXXXX"; |
| 713 char temp_file[sizeof(temp_file_template)]; | 860 char temp_file[sizeof(temp_file_template)]; |
| 714 int temp_file_fd = -1; | 861 int temp_file_fd = -1; |
| 715 if (info.upload) { | 862 if (keep_fd) { |
| 716 memcpy(temp_file, temp_file_template, sizeof(temp_file_template)); | 863 temp_file_fd = dumpfd; |
| 717 | 864 // Rewind the destination, we are going to overwrite it. |
| 718 for (unsigned i = 0; i < 10; ++i) { | 865 if (lseek(dumpfd, 0, SEEK_SET) == -1) { |
| 719 uint64_t t; | 866 static const char msg[] = "Cannot upload crash dump: failed to " |
| 720 sys_read(ufd, &t, sizeof(t)); | 867 "reposition minidump FD (2)\n"; |
| 721 write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t); | |
| 722 | |
| 723 temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600); | |
| 724 if (temp_file_fd >= 0) | |
| 725 break; | |
| 726 } | |
| 727 | |
| 728 if (temp_file_fd < 0) { | |
| 729 static const char msg[] = "Failed to create temporary file in /tmp: " | |
| 730 "cannot upload crash dump\n"; | |
| 731 WriteLog(msg, sizeof(msg) - 1); | 868 WriteLog(msg, sizeof(msg) - 1); |
| 732 IGNORE_RET(sys_close(ufd)); | 869 IGNORE_RET(sys_close(dumpfd)); |
| 733 return; | 870 return; |
| 734 } | 871 } |
| 735 } else { | 872 } else { |
| 736 temp_file_fd = sys_open(info.filename, O_WRONLY, 0600); | 873 if (info.upload) { |
| 737 if (temp_file_fd < 0) { | 874 memcpy(temp_file, temp_file_template, sizeof(temp_file_template)); |
| 738 static const char msg[] = "Failed to save crash dump: failed to open\n"; | 875 |
| 739 WriteLog(msg, sizeof(msg) - 1); | 876 for (unsigned i = 0; i < 10; ++i) { |
| 740 IGNORE_RET(sys_close(ufd)); | 877 uint64_t t; |
| 741 return; | 878 sys_read(ufd, &t, sizeof(t)); |
| 879 write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t); | |
| 880 | |
| 881 temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600); | |
| 882 if (temp_file_fd >= 0) | |
| 883 break; | |
| 884 } | |
| 885 | |
| 886 if (temp_file_fd < 0) { | |
| 887 static const char msg[] = "Failed to create temporary file in /tmp: " | |
| 888 "cannot upload crash dump\n"; | |
| 889 WriteLog(msg, sizeof(msg) - 1); | |
| 890 IGNORE_RET(sys_close(ufd)); | |
| 891 return; | |
| 892 } | |
| 893 } else { | |
| 894 temp_file_fd = sys_open(info.filename, O_WRONLY, 0600); | |
| 895 if (temp_file_fd < 0) { | |
| 896 static const char msg[] = "Failed to save crash dump: failed to open\n"; | |
| 897 WriteLog(msg, sizeof(msg) - 1); | |
| 898 IGNORE_RET(sys_close(ufd)); | |
| 899 return; | |
| 900 } | |
| 742 } | 901 } |
| 743 } | 902 } |
| 744 | 903 |
| 745 // The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL. | 904 // The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL. |
| 746 char mime_boundary[28 + 16 + 1]; | 905 char mime_boundary[28 + 16 + 1]; |
| 747 my_memset(mime_boundary, '-', 28); | 906 my_memset(mime_boundary, '-', 28); |
| 748 uint64_t boundary_rand; | 907 uint64_t boundary_rand; |
| 749 sys_read(ufd, &boundary_rand, sizeof(boundary_rand)); | 908 sys_read(ufd, &boundary_rand, sizeof(boundary_rand)); |
| 750 write_uint64_hex(mime_boundary + 28, boundary_rand); | 909 write_uint64_hex(mime_boundary + 28, boundary_rand); |
| 751 mime_boundary[28 + 16] = 0; | 910 mime_boundary[28 + 16] = 0; |
| (...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1061 // Append a multipart boundary and the contents of the AddressSanitizer log. | 1220 // Append a multipart boundary and the contents of the AddressSanitizer log. |
| 1062 writer.AddBoundary(); | 1221 writer.AddBoundary(); |
| 1063 writer.AddFileContents(g_log_msg, log_data, log_size); | 1222 writer.AddFileContents(g_log_msg, log_data, log_size); |
| 1064 #endif | 1223 #endif |
| 1065 writer.AddEnd(); | 1224 writer.AddEnd(); |
| 1066 writer.Flush(); | 1225 writer.Flush(); |
| 1067 | 1226 |
| 1068 IGNORE_RET(sys_close(temp_file_fd)); | 1227 IGNORE_RET(sys_close(temp_file_fd)); |
| 1069 | 1228 |
| 1070 #if defined(OS_ANDROID) | 1229 #if defined(OS_ANDROID) |
| 1071 __android_log_write(ANDROID_LOG_WARN, | 1230 if (info.filename) { |
| 1072 kGoogleBreakpad, | 1231 int filename_length = my_strlen(info.filename); |
| 1073 "Output crash dump file:"); | |
| 1074 __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, info.filename); | |
| 1075 | 1232 |
| 1076 char pid_buf[kUint64StringSize]; | 1233 // If this was a file, we need to copy it to the right place and use the |
| 1077 uint64_t pid_str_len = my_uint64_len(info.pid); | 1234 // right file name so it gets uploaded by the browser. |
| 1078 my_uint64tos(pid_buf, info.pid, pid_str_len); | 1235 const char msg[] = "Output crash dump file:"; |
| 1236 WriteLog(msg, sizeof(msg) - 1); | |
| 1237 WriteLog(info.filename, filename_length - 1); | |
| 1079 | 1238 |
| 1080 // -1 because we won't need the null terminator on the original filename. | 1239 char pid_buf[kUint64StringSize]; |
| 1081 size_t done_filename_len = my_strlen(info.filename) + pid_str_len - 1; | 1240 uint64_t pid_str_length = my_uint64_len(info.pid); |
| 1082 char* done_filename = reinterpret_cast<char*>( | 1241 my_uint64tos(pid_buf, info.pid, pid_str_length); |
| 1083 allocator.Alloc(done_filename_len)); | 1242 |
| 1084 // Rename the file such that the pid is the suffix in order to signal other | 1243 // -1 because we won't need the null terminator on the original filename. |
| 1085 // processes that the minidump is complete. The advantage of using the pid as | 1244 unsigned done_filename_len = filename_length -1 + pid_str_length; |
| 1086 // the suffix is that it is trivial to associate the minidump with the | 1245 char* done_filename = reinterpret_cast<char*>( |
| 1087 // crashed process. | 1246 allocator.Alloc(done_filename_len)); |
| 1088 // Finally, note strncpy prevents null terminators from | 1247 // Rename the file such that the pid is the suffix in order signal to other |
| 1089 // being copied. Pad the rest with 0's. | 1248 // processes that the minidump is complete. The advantage of using the pid |
| 1090 my_strncpy(done_filename, info.filename, done_filename_len); | 1249 // as the suffix is that it is trivial to associate the minidump with the |
| 1091 // Append the suffix a null terminator should be added. | 1250 // crashed process. |
| 1092 my_strncat(done_filename, pid_buf, pid_str_len); | 1251 // Finally, note strncpy prevents null terminators from |
| 1093 // Rename the minidump file to signal that it is complete. | 1252 // being copied. Pad the rest with 0's. |
| 1094 if (rename(info.filename, done_filename)) { | 1253 my_strncpy(done_filename, info.filename, done_filename_len); |
| 1095 __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, "Failed to rename:"); | 1254 // Append the suffix a null terminator should be added. |
| 1096 __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, info.filename); | 1255 my_strncat(done_filename, pid_buf, pid_str_length); |
| 1097 __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, "to"); | 1256 // Rename the minidump file to signal that it is complete. |
| 1098 __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, done_filename); | 1257 if (rename(info.filename, done_filename)) { |
| 1258 const char failed_msg[] = "Failed to rename:"; | |
| 1259 WriteLog(failed_msg, sizeof(failed_msg) - 1); | |
| 1260 WriteLog(info.filename, filename_length - 1); | |
| 1261 const char to_msg[] = "to"; | |
| 1262 WriteLog(to_msg, sizeof(to_msg) - 1); | |
| 1263 WriteLog(done_filename, done_filename_len - 1); | |
| 1264 } | |
| 1099 } | 1265 } |
| 1100 #endif | 1266 #endif |
| 1101 | 1267 |
| 1102 if (!info.upload) | 1268 if (!info.upload) |
| 1103 return; | 1269 return; |
| 1104 | 1270 |
| 1105 // The --header argument to wget looks like: | 1271 // The --header argument to wget looks like: |
| 1106 // --header=Content-Type: multipart/form-data; boundary=XYZ | 1272 // --header=Content-Type: multipart/form-data; boundary=XYZ |
| 1107 // where the boundary has two fewer leading '-' chars | 1273 // where the boundary has two fewer leading '-' chars |
| 1108 static const char header_msg[] = | 1274 static const char header_msg[] = |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1269 const std::string process_type = | 1435 const std::string process_type = |
| 1270 parsed_command_line.GetSwitchValueASCII(switches::kProcessType); | 1436 parsed_command_line.GetSwitchValueASCII(switches::kProcessType); |
| 1271 if (process_type.empty()) { | 1437 if (process_type.empty()) { |
| 1272 EnableCrashDumping(getenv(env_vars::kHeadless) != NULL); | 1438 EnableCrashDumping(getenv(env_vars::kHeadless) != NULL); |
| 1273 } else if (process_type == switches::kRendererProcess || | 1439 } else if (process_type == switches::kRendererProcess || |
| 1274 process_type == switches::kPluginProcess || | 1440 process_type == switches::kPluginProcess || |
| 1275 process_type == switches::kPpapiPluginProcess || | 1441 process_type == switches::kPpapiPluginProcess || |
| 1276 process_type == switches::kZygoteProcess || | 1442 process_type == switches::kZygoteProcess || |
| 1277 process_type == switches::kGpuProcess) { | 1443 process_type == switches::kGpuProcess) { |
| 1278 #if defined(OS_ANDROID) | 1444 #if defined(OS_ANDROID) |
| 1279 child_process_logging::SetClientId("Android"); | 1445 NOTREACHED() << "Breakpad initialized with InitCrashReporter() instead of " |
| 1280 #endif | 1446 "InitNonBrowserCrashReporter in non browser process."; |
|
Lei Zhang
2012/10/23 21:34:58
slightly better to say "... in " << process_type <
Jay Civelli
2012/10/24 00:12:53
Done.
| |
| 1447 return; | |
| 1448 #else | |
| 1281 // We might be chrooted in a zygote or renderer process so we cannot call | 1449 // We might be chrooted in a zygote or renderer process so we cannot call |
| 1282 // GetCollectStatsConsent because that needs access the the user's home | 1450 // GetCollectStatsConsent because that needs access the the user's home |
| 1283 // dir. Instead, we set a command line flag for these processes. | 1451 // dir. Instead, we set a command line flag for these processes. |
| 1284 // Even though plugins are not chrooted, we share the same code path for | 1452 // Even though plugins are not chrooted, we share the same code path for |
| 1285 // simplicity. | 1453 // simplicity. |
| 1286 if (!parsed_command_line.HasSwitch(switches::kEnableCrashReporter)) | 1454 if (!parsed_command_line.HasSwitch(switches::kEnableCrashReporter)) |
| 1287 return; | 1455 return; |
| 1288 // Get the guid and linux distro from the command line switch. | 1456 // Get the guid and linux distro from the command line switch. |
| 1289 std::string switch_value = | 1457 std::string switch_value = |
| 1290 parsed_command_line.GetSwitchValueASCII(switches::kEnableCrashReporter); | 1458 parsed_command_line.GetSwitchValueASCII(switches::kEnableCrashReporter); |
| 1291 size_t separator = switch_value.find(","); | 1459 size_t separator = switch_value.find(","); |
| 1292 if (separator != std::string::npos) { | 1460 if (separator != std::string::npos) { |
| 1293 child_process_logging::SetClientId(switch_value.substr(0, separator)); | 1461 child_process_logging::SetClientId(switch_value.substr(0, separator)); |
| 1294 base::SetLinuxDistro(switch_value.substr(separator + 1)); | 1462 base::SetLinuxDistro(switch_value.substr(separator + 1)); |
| 1295 } else { | 1463 } else { |
| 1296 child_process_logging::SetClientId(switch_value); | 1464 child_process_logging::SetClientId(switch_value); |
| 1297 } | 1465 } |
| 1298 EnableNonBrowserCrashDumping(); | 1466 EnableNonBrowserCrashDumping(); |
| 1467 LOG(INFO) << "Non Browser crash dumping enabled for: " << process_type; | |
|
Lei Zhang
2012/10/23 21:34:58
VLOG() ?
Jay Civelli
2012/10/24 00:12:53
Done.
| |
| 1468 #endif // #if defined(OS_ANDROID) | |
| 1299 } | 1469 } |
| 1300 | 1470 |
| 1301 // Set the base process start time value. | 1471 SetProcessStartTime(); |
| 1302 struct timeval tv; | |
| 1303 if (!gettimeofday(&tv, NULL)) | |
| 1304 g_process_start_time = timeval_to_ms(&tv); | |
| 1305 else | |
| 1306 g_process_start_time = 0; | |
| 1307 | 1472 |
| 1308 logging::SetDumpWithoutCrashingFunction(&DumpProcess); | 1473 logging::SetDumpWithoutCrashingFunction(&DumpProcess); |
| 1309 #if defined(ADDRESS_SANITIZER) | 1474 #if defined(ADDRESS_SANITIZER) |
| 1310 // Register the callback for AddressSanitizer error reporting. | 1475 // Register the callback for AddressSanitizer error reporting. |
| 1311 __asan_set_error_report_callback(AsanLinuxBreakpadCallback); | 1476 __asan_set_error_report_callback(AsanLinuxBreakpadCallback); |
| 1312 #endif | 1477 #endif |
| 1313 } | 1478 } |
| 1314 | 1479 |
| 1480 #if defined(OS_ANDROID) | |
| 1481 void InitNonBrowserCrashReporterForAndroid(int minidump_fd) { | |
| 1482 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
| 1483 if (command_line->HasSwitch(switches::kEnableCrashReporter)) | |
| 1484 EnableNonBrowserCrashDumping(minidump_fd); | |
| 1485 } | |
| 1486 #endif // OS_ANDROID | |
| 1487 | |
| 1315 bool IsCrashReporterEnabled() { | 1488 bool IsCrashReporterEnabled() { |
| 1316 return g_is_crash_reporter_enabled; | 1489 return g_is_crash_reporter_enabled; |
| 1317 } | 1490 } |
| OLD | NEW |