Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 | 7 |
| 8 #include "components/breakpad/app/breakpad_linux.h" | 8 #include "components/breakpad/app/breakpad_linux.h" |
| 9 | 9 |
| 10 #include <fcntl.h> | 10 #include <fcntl.h> |
| (...skipping 279 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 290 void AddItemWithoutTrailingSpaces(const void* base, size_t size); | 290 void AddItemWithoutTrailingSpaces(const void* base, size_t size); |
| 291 | 291 |
| 292 struct kernel_iovec iov_[kIovCapacity]; | 292 struct kernel_iovec iov_[kIovCapacity]; |
| 293 int iov_index_; | 293 int iov_index_; |
| 294 | 294 |
| 295 // Output file descriptor. | 295 // Output file descriptor. |
| 296 int fd_; | 296 int fd_; |
| 297 | 297 |
| 298 const char* const mime_boundary_; | 298 const char* const mime_boundary_; |
| 299 | 299 |
| 300 private: | |
| 300 DISALLOW_COPY_AND_ASSIGN(MimeWriter); | 301 DISALLOW_COPY_AND_ASSIGN(MimeWriter); |
| 301 }; | 302 }; |
| 302 | 303 |
| 303 MimeWriter::MimeWriter(int fd, const char* const mime_boundary) | 304 MimeWriter::MimeWriter(int fd, const char* const mime_boundary) |
| 304 : iov_index_(0), | 305 : iov_index_(0), |
| 305 fd_(fd), | 306 fd_(fd), |
| 306 mime_boundary_(mime_boundary) { | 307 mime_boundary_(mime_boundary) { |
| 307 } | 308 } |
| 308 | 309 |
| 309 MimeWriter::~MimeWriter() { | 310 MimeWriter::~MimeWriter() { |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 396 | 397 |
| 397 void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) { | 398 void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) { |
| 398 AddItem(base, LengthWithoutTrailingSpaces(static_cast<const char*>(base), | 399 AddItem(base, LengthWithoutTrailingSpaces(static_cast<const char*>(base), |
| 399 size)); | 400 size)); |
| 400 } | 401 } |
| 401 | 402 |
| 402 #if defined(OS_CHROMEOS) | 403 #if defined(OS_CHROMEOS) |
| 403 // This subclass is used on Chromium OS to report crashes in a format easy for | 404 // This subclass is used on Chromium OS to report crashes in a format easy for |
| 404 // the central crash reporting facility to understand. | 405 // the central crash reporting facility to understand. |
| 405 // Format is <name>:<data length in decimal>:<data> | 406 // Format is <name>:<data length in decimal>:<data> |
| 406 class CrashReporterWriter : public MimeWriter | 407 class CrashReporterWriter : public MimeWriter { |
| 407 { | |
| 408 public: | 408 public: |
| 409 explicit CrashReporterWriter(int fd); | 409 explicit CrashReporterWriter(int fd); |
| 410 | 410 |
| 411 virtual void AddBoundary() OVERRIDE; | 411 virtual void AddBoundary() OVERRIDE; |
| 412 | 412 |
| 413 virtual void AddEnd() OVERRIDE; | 413 virtual void AddEnd() OVERRIDE; |
| 414 | 414 |
| 415 virtual void AddPairData(const char* msg_type, | 415 virtual void AddPairData(const char* msg_type, |
| 416 size_t msg_type_size, | 416 size_t msg_type_size, |
| 417 const char* msg_data, | 417 const char* msg_data, |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 525 #endif | 525 #endif |
| 526 | 526 |
| 527 size_t WriteLog(const char* buf, size_t nbytes) { | 527 size_t WriteLog(const char* buf, size_t nbytes) { |
| 528 #if defined(OS_ANDROID) | 528 #if defined(OS_ANDROID) |
| 529 return __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, buf); | 529 return __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, buf); |
| 530 #else | 530 #else |
| 531 return sys_write(2, buf, nbytes); | 531 return sys_write(2, buf, nbytes); |
| 532 #endif | 532 #endif |
| 533 } | 533 } |
| 534 | 534 |
| 535 size_t WriteNewline() { | |
| 536 return WriteLog("\n", 1); | |
| 537 } | |
| 538 | |
| 535 #if defined(OS_ANDROID) | 539 #if defined(OS_ANDROID) |
| 536 // Android's native crash handler outputs a diagnostic tombstone to the device | 540 // Android's native crash handler outputs a diagnostic tombstone to the device |
| 537 // log. By returning false from the HandlerCallbacks, breakpad will reinstall | 541 // log. By returning false from the HandlerCallbacks, breakpad will reinstall |
| 538 // the previous (i.e. native) signal handlers before returning from its own | 542 // the previous (i.e. native) signal handlers before returning from its own |
| 539 // handler. A Chrome build fingerprint is written to the log, so that the | 543 // handler. A Chrome build fingerprint is written to the log, so that the |
| 540 // specific build of Chrome and the location of the archived Chrome symbols can | 544 // specific build of Chrome and the location of the archived Chrome symbols can |
| 541 // be determined directly from it. | 545 // be determined directly from it. |
| 542 bool FinalizeCrashDoneAndroid() { | 546 bool FinalizeCrashDoneAndroid() { |
| 543 base::android::BuildInfo* android_build_info = | 547 base::android::BuildInfo* android_build_info = |
| 544 base::android::BuildInfo::GetInstance(); | 548 base::android::BuildInfo::GetInstance(); |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 699 info.distro_length = distro_length; | 703 info.distro_length = distro_length; |
| 700 info.upload = false; | 704 info.upload = false; |
| 701 info.process_start_time = g_process_start_time; | 705 info.process_start_time = g_process_start_time; |
| 702 info.pid = g_pid; | 706 info.pid = g_pid; |
| 703 info.crash_keys = g_crash_keys; | 707 info.crash_keys = g_crash_keys; |
| 704 HandleCrashDump(info); | 708 HandleCrashDump(info); |
| 705 bool finalize_result = FinalizeCrashDoneAndroid(); | 709 bool finalize_result = FinalizeCrashDoneAndroid(); |
| 706 base::android::BuildInfo* android_build_info = | 710 base::android::BuildInfo* android_build_info = |
| 707 base::android::BuildInfo::GetInstance(); | 711 base::android::BuildInfo::GetInstance(); |
| 708 if (android_build_info->sdk_int() >= 18 && | 712 if (android_build_info->sdk_int() >= 18 && |
| 709 strcmp(android_build_info->build_type(), "eng") != 0 && | 713 my_strcmp(android_build_info->build_type(), "eng") != 0 && |
| 710 strcmp(android_build_info->build_type(), "userdebug") != 0) { | 714 my_strcmp(android_build_info->build_type(), "userdebug") != 0) { |
| 711 // On JB MR2 and later, the system crash handler displays a dialog. For | 715 // On JB MR2 and later, the system crash handler displays a dialog. For |
| 712 // renderer crashes, this is a bad user experience and so this is disabled | 716 // renderer crashes, this is a bad user experience and so this is disabled |
| 713 // for user builds of Android. | 717 // for user builds of Android. |
| 714 // TODO(cjhopman): There should be some way to recover the crash stack from | 718 // TODO(cjhopman): There should be some way to recover the crash stack from |
| 715 // non-uploading user clients. See http://crbug.com/273706. | 719 // non-uploading user clients. See http://crbug.com/273706. |
| 716 __android_log_write(ANDROID_LOG_WARN, | 720 __android_log_write(ANDROID_LOG_WARN, |
| 717 kGoogleBreakpad, | 721 kGoogleBreakpad, |
| 718 "Tombstones are disabled on JB MR2+ user builds."); | 722 "Tombstones are disabled on JB MR2+ user builds."); |
| 719 __android_log_write(ANDROID_LOG_WARN, | 723 __android_log_write(ANDROID_LOG_WARN, |
| 720 kGoogleBreakpad, | 724 kGoogleBreakpad, |
| (...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1013 NULL, | 1017 NULL, |
| 1014 }; | 1018 }; |
| 1015 static const char msg[] = "Cannot upload crash dump: cannot exec " | 1019 static const char msg[] = "Cannot upload crash dump: cannot exec " |
| 1016 "/usr/bin/wget\n"; | 1020 "/usr/bin/wget\n"; |
| 1017 #endif | 1021 #endif |
| 1018 execve(args[0], const_cast<char**>(args), environ); | 1022 execve(args[0], const_cast<char**>(args), environ); |
| 1019 WriteLog(msg, sizeof(msg) - 1); | 1023 WriteLog(msg, sizeof(msg) - 1); |
| 1020 sys__exit(1); | 1024 sys__exit(1); |
| 1021 } | 1025 } |
| 1022 | 1026 |
| 1027 // Runs in the helper process to wait for the upload process running | |
| 1028 // ExecUploadProcessOrTerminate() to finish. Returns the number of bytes written | |
| 1029 // to |fd| and save the written contents to |buf|. | |
| 1030 // |buf| needs to be big enough to hold |bytes_to_read| + 1 characters. | |
| 1031 size_t WaitForCrashReportUploadProcess(int fd, size_t bytes_to_read, | |
| 1032 char* buf) { | |
| 1033 size_t bytes_read = 0; | |
| 1034 | |
| 1035 // Upload should finish in about 10 seconds. Add a few more 500 ms | |
| 1036 // internals to account for process startup time. | |
| 1037 for (size_t wait_count = 0; wait_count < 24; ++wait_count) { | |
| 1038 struct kernel_pollfd poll_fd; | |
| 1039 poll_fd.fd = fd; | |
| 1040 poll_fd.events = POLLIN | POLLPRI | POLLERR; | |
| 1041 int ret = sys_poll(&poll_fd, 1, 500); | |
| 1042 if (ret < 0) { | |
| 1043 // Error | |
| 1044 break; | |
| 1045 } else if (ret > 0) { | |
| 1046 // There is data to read. | |
| 1047 ssize_t len = HANDLE_EINTR( | |
| 1048 sys_read(fd, buf + bytes_read, bytes_to_read - bytes_read)); | |
| 1049 if (len < 0) | |
| 1050 break; | |
| 1051 bytes_read += len; | |
| 1052 if (bytes_read == bytes_to_read) | |
| 1053 break; | |
| 1054 } | |
| 1055 // |ret| == 0 -> timed out, continue waiting. | |
| 1056 // or |bytes_read| < |bytes_to_read| still, keep reading. | |
| 1057 } | |
| 1058 buf[bytes_to_read] = 0; // Always NULL terminate the buffer. | |
|
vapier
2014/04/24 00:47:39
you NUL terminate the buffer ;)
Lei Zhang
2014/04/24 01:30:47
Done.
| |
| 1059 return bytes_read; | |
| 1060 } | |
| 1061 | |
| 1062 // |buf| should be |expected_len| + 1 characters in size and NULL terminated. | |
| 1063 bool IsValidCrashReportId(const char* buf, size_t bytes_read, | |
| 1064 size_t expected_len) { | |
| 1065 if (bytes_read != expected_len) | |
| 1066 return false; | |
| 1067 #if defined(OS_CHROMEOS) | |
| 1068 return my_strcmp(buf, "_sys_cr_finished") == 0; | |
| 1069 #else | |
| 1070 for (size_t i = 0; i <= bytes_read; ++i) { | |
| 1071 char c = buf[i]; | |
| 1072 if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) | |
|
vapier
2014/04/24 00:47:39
i was suggesting ctypes also to avoid case issues.
Lei Zhang
2014/04/24 01:30:47
Done. It's all lower case right now, but why not h
| |
| 1073 continue; | |
| 1074 return false; | |
| 1075 } | |
| 1076 return true; | |
| 1077 #endif | |
| 1078 } | |
| 1079 | |
| 1080 // |buf| should be |expected_len| + 1 characters in size and NULL terminated. | |
| 1081 void HandleCrashReportId(const char* buf, size_t bytes_read, | |
| 1082 size_t expected_len) { | |
| 1083 WriteNewline(); | |
| 1084 if (!IsValidCrashReportId(buf, bytes_read, expected_len)) { | |
| 1085 #if defined(OS_CHROMEOS) | |
| 1086 static const char msg[] = "Crash_reporter failed to process crash report"; | |
| 1087 #else | |
| 1088 static const char msg[] = "Failed to get crash dump id."; | |
| 1089 #endif | |
| 1090 WriteLog(msg, sizeof(msg) - 1); | |
| 1091 WriteNewline(); | |
|
vapier
2014/04/24 00:47:39
why not embed the newline ? you do below ...
Lei Zhang
2014/04/24 01:30:47
WriteLog() below is a bit more complicated. There'
| |
| 1092 return; | |
| 1093 } | |
| 1094 | |
| 1095 #if defined(OS_CHROMEOS) | |
| 1096 static const char msg[] = "Crash dump received by crash_reporter\n"; | |
| 1097 WriteLog(msg, sizeof(msg) - 1); | |
| 1098 #else | |
| 1099 // Write crash dump id to stderr. | |
| 1100 static const char msg[] = "Crash dump id: "; | |
| 1101 WriteLog(msg, sizeof(msg) - 1); | |
| 1102 WriteLog(buf, my_strlen(buf)); | |
| 1103 WriteNewline(); | |
| 1104 | |
| 1105 // Write crash dump id to crash log as: seconds_since_epoch,crash_id | |
| 1106 struct kernel_timeval tv; | |
| 1107 if (g_crash_log_path && !sys_gettimeofday(&tv, NULL)) { | |
| 1108 uint64_t time = kernel_timeval_to_ms(&tv) / 1000; | |
| 1109 char time_str[kUint64StringSize]; | |
| 1110 const unsigned time_len = my_uint64_len(time); | |
| 1111 my_uint64tos(time_str, time, time_len); | |
| 1112 | |
| 1113 const int kLogOpenFlags = O_CREAT | O_WRONLY | O_APPEND; | |
|
vapier
2014/04/24 00:47:39
in general, any reason to not use O_CLOEXEC ?
in
Lei Zhang
2014/04/24 01:30:47
We probably wrote this when it was still brand new
vapier
2014/04/24 02:16:48
it frequently doesn't matter, but O_CLOEXEC is a d
| |
| 1114 int log_fd = sys_open(g_crash_log_path, kLogOpenFlags, 0600); | |
| 1115 if (log_fd > 0) { | |
| 1116 sys_write(log_fd, time_str, time_len); | |
| 1117 sys_write(log_fd, ",", 1); | |
| 1118 sys_write(log_fd, buf, my_strlen(buf)); | |
| 1119 sys_write(log_fd, "\n", 1); | |
| 1120 IGNORE_RET(sys_close(log_fd)); | |
| 1121 } | |
| 1122 } | |
| 1123 #endif | |
| 1124 } | |
| 1125 | |
| 1023 #if defined(OS_CHROMEOS) | 1126 #if defined(OS_CHROMEOS) |
| 1024 const char* GetCrashingProcessName(const BreakpadInfo& info, | 1127 const char* GetCrashingProcessName(const BreakpadInfo& info, |
| 1025 google_breakpad::PageAllocator* allocator) { | 1128 google_breakpad::PageAllocator* allocator) { |
| 1026 // Symlink to process binary is at /proc/###/exe. | 1129 // Symlink to process binary is at /proc/###/exe. |
| 1027 char linkpath[kUint64StringSize + sizeof("/proc/") + sizeof("/exe")] = | 1130 char linkpath[kUint64StringSize + sizeof("/proc/") + sizeof("/exe")] = |
| 1028 "/proc/"; | 1131 "/proc/"; |
| 1029 uint64_t pid_value_len = my_uint64_len(info.pid); | 1132 uint64_t pid_value_len = my_uint64_len(info.pid); |
| 1030 my_uint64tos(linkpath + sizeof("/proc/") - 1, info.pid, pid_value_len); | 1133 my_uint64tos(linkpath + sizeof("/proc/") - 1, info.pid, pid_value_len); |
| 1031 linkpath[sizeof("/proc/") - 1 + pid_value_len] = '\0'; | 1134 linkpath[sizeof("/proc/") - 1 + pid_value_len] = '\0'; |
| 1032 my_strlcat(linkpath, "/exe", sizeof(linkpath)); | 1135 my_strlcat(linkpath, "/exe", sizeof(linkpath)); |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1198 // Content-Type: application/octet-stream \r\n \r\n | 1301 // Content-Type: application/octet-stream \r\n \r\n |
| 1199 // <dump contents> | 1302 // <dump contents> |
| 1200 // \r\n BOUNDARY -- \r\n | 1303 // \r\n BOUNDARY -- \r\n |
| 1201 | 1304 |
| 1202 #if defined(OS_CHROMEOS) | 1305 #if defined(OS_CHROMEOS) |
| 1203 CrashReporterWriter writer(temp_file_fd); | 1306 CrashReporterWriter writer(temp_file_fd); |
| 1204 #else | 1307 #else |
| 1205 MimeWriter writer(temp_file_fd, mime_boundary); | 1308 MimeWriter writer(temp_file_fd, mime_boundary); |
| 1206 #endif | 1309 #endif |
| 1207 { | 1310 { |
| 1311 // TODO(thestig) Do not use this inside a compromised context. | |
| 1208 std::string product_name; | 1312 std::string product_name; |
| 1209 std::string version; | 1313 std::string version; |
| 1210 | 1314 |
| 1211 GetBreakpadClient()->GetProductNameAndVersion(&product_name, &version); | 1315 GetBreakpadClient()->GetProductNameAndVersion(&product_name, &version); |
| 1212 | 1316 |
| 1213 writer.AddBoundary(); | 1317 writer.AddBoundary(); |
| 1214 writer.AddPairString("prod", product_name.c_str()); | 1318 writer.AddPairString("prod", product_name.c_str()); |
| 1215 writer.AddBoundary(); | 1319 writer.AddBoundary(); |
| 1216 writer.AddPairString("ver", version.c_str()); | 1320 writer.AddPairString("ver", version.c_str()); |
| 1217 writer.AddBoundary(); | 1321 writer.AddBoundary(); |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1403 // Upload process. | 1507 // Upload process. |
| 1404 IGNORE_RET(sys_close(fds[0])); | 1508 IGNORE_RET(sys_close(fds[0])); |
| 1405 IGNORE_RET(sys_dup2(fds[1], 3)); | 1509 IGNORE_RET(sys_dup2(fds[1], 3)); |
| 1406 ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf, | 1510 ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf, |
| 1407 &allocator); | 1511 &allocator); |
| 1408 } | 1512 } |
| 1409 | 1513 |
| 1410 // Helper process. | 1514 // Helper process. |
| 1411 if (upload_child > 0) { | 1515 if (upload_child > 0) { |
| 1412 IGNORE_RET(sys_close(fds[1])); | 1516 IGNORE_RET(sys_close(fds[1])); |
| 1413 char id_buf[17]; // Crash report IDs are expected to be 16 chars. | |
| 1414 ssize_t len = -1; | |
| 1415 // Upload should finish in about 10 seconds. Add a few more 500 ms | |
| 1416 // internals to account for process startup time. | |
| 1417 for (size_t wait_count = 0; wait_count < 24; ++wait_count) { | |
| 1418 struct kernel_pollfd poll_fd; | |
| 1419 poll_fd.fd = fds[0]; | |
| 1420 poll_fd.events = POLLIN | POLLPRI | POLLERR; | |
| 1421 int ret = sys_poll(&poll_fd, 1, 500); | |
| 1422 if (ret < 0) { | |
| 1423 // Error | |
| 1424 break; | |
| 1425 } else if (ret > 0) { | |
| 1426 // There is data to read. | |
| 1427 len = HANDLE_EINTR(sys_read(fds[0], id_buf, sizeof(id_buf) - 1)); | |
| 1428 break; | |
| 1429 } | |
| 1430 // ret == 0 -> timed out, continue waiting. | |
| 1431 } | |
| 1432 if (len > 0) { | |
| 1433 // Write crash dump id to stderr. | |
| 1434 id_buf[len] = 0; | |
| 1435 static const char msg[] = "\nCrash dump id: "; | |
| 1436 WriteLog(msg, sizeof(msg) - 1); | |
| 1437 WriteLog(id_buf, my_strlen(id_buf)); | |
| 1438 WriteLog("\n", 1); | |
| 1439 | 1517 |
| 1440 // Write crash dump id to crash log as: seconds_since_epoch,crash_id | 1518 const size_t kCrashIdLength = 16; |
| 1441 struct kernel_timeval tv; | 1519 char id_buf[kCrashIdLength + 1]; |
| 1442 if (g_crash_log_path && !sys_gettimeofday(&tv, NULL)) { | 1520 size_t bytes_read = |
| 1443 uint64_t time = kernel_timeval_to_ms(&tv) / 1000; | 1521 WaitForCrashReportUploadProcess(fds[0], kCrashIdLength, id_buf); |
| 1444 char time_str[kUint64StringSize]; | 1522 HandleCrashReportId(id_buf, bytes_read, kCrashIdLength); |
| 1445 const unsigned time_len = my_uint64_len(time); | |
| 1446 my_uint64tos(time_str, time, time_len); | |
| 1447 | 1523 |
| 1448 int log_fd = sys_open(g_crash_log_path, | |
| 1449 O_CREAT | O_WRONLY | O_APPEND, | |
| 1450 0600); | |
| 1451 if (log_fd > 0) { | |
| 1452 sys_write(log_fd, time_str, time_len); | |
| 1453 sys_write(log_fd, ",", 1); | |
| 1454 sys_write(log_fd, id_buf, my_strlen(id_buf)); | |
| 1455 sys_write(log_fd, "\n", 1); | |
| 1456 IGNORE_RET(sys_close(log_fd)); | |
| 1457 } | |
| 1458 } | |
| 1459 } | |
| 1460 if (sys_waitpid(upload_child, NULL, WNOHANG) == 0) { | 1524 if (sys_waitpid(upload_child, NULL, WNOHANG) == 0) { |
| 1461 // Upload process is still around, kill it. | 1525 // Upload process is still around, kill it. |
| 1462 sys_kill(upload_child, SIGKILL); | 1526 sys_kill(upload_child, SIGKILL); |
| 1463 } | 1527 } |
| 1464 } | 1528 } |
| 1465 } | 1529 } |
| 1466 | 1530 |
| 1467 // Helper process. | 1531 // Helper process. |
| 1468 IGNORE_RET(sys_unlink(info.filename)); | 1532 IGNORE_RET(sys_unlink(info.filename)); |
| 1469 #if defined(ADDRESS_SANITIZER) | 1533 #if defined(ADDRESS_SANITIZER) |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1545 } | 1609 } |
| 1546 } | 1610 } |
| 1547 } | 1611 } |
| 1548 #endif // OS_ANDROID | 1612 #endif // OS_ANDROID |
| 1549 | 1613 |
| 1550 bool IsCrashReporterEnabled() { | 1614 bool IsCrashReporterEnabled() { |
| 1551 return g_is_crash_reporter_enabled; | 1615 return g_is_crash_reporter_enabled; |
| 1552 } | 1616 } |
| 1553 | 1617 |
| 1554 } // namespace breakpad | 1618 } // namespace breakpad |
| OLD | NEW |