summaryrefslogtreecommitdiffstats
path: root/components/crash/content
diff options
context:
space:
mode:
Diffstat (limited to 'components/crash/content')
-rw-r--r--components/crash/content/app/BUILD.gn79
-rw-r--r--components/crash/content/app/DEPS8
-rw-r--r--components/crash/content/app/breakpad_linux.cc1760
-rw-r--r--components/crash/content/app/breakpad_linux.h32
-rw-r--r--components/crash/content/app/breakpad_linux_impl.h65
-rw-r--r--components/crash/content/app/breakpad_mac.h27
-rw-r--r--components/crash/content/app/breakpad_mac.mm286
-rw-r--r--components/crash/content/app/breakpad_mac_stubs.mm24
-rw-r--r--components/crash/content/app/breakpad_win.cc745
-rw-r--r--components/crash/content/app/breakpad_win.h28
-rw-r--r--components/crash/content/app/crash_keys_win.cc192
-rw-r--r--components/crash/content/app/crash_keys_win.h87
-rw-r--r--components/crash/content/app/crash_keys_win_unittest.cc142
-rw-r--r--components/crash/content/app/crash_reporter_client.cc148
-rw-r--r--components/crash/content/app/crash_reporter_client.h158
-rw-r--r--components/crash/content/app/crashpad_mac.h54
-rw-r--r--components/crash/content/app/crashpad_mac.mm251
-rw-r--r--components/crash/content/app/hard_error_handler_win.cc112
-rw-r--r--components/crash/content/app/hard_error_handler_win.h34
-rw-r--r--components/crash/content/browser/BUILD.gn38
-rw-r--r--components/crash/content/browser/DEPS4
-rw-r--r--components/crash/content/browser/crash_dump_manager_android.cc174
-rw-r--r--components/crash/content/browser/crash_dump_manager_android.h85
-rw-r--r--components/crash/content/browser/crash_handler_host_linux.cc437
-rw-r--r--components/crash/content/browser/crash_handler_host_linux.h93
-rw-r--r--components/crash/content/tools/BUILD.gn18
-rw-r--r--components/crash/content/tools/crash_service.cc487
-rw-r--r--components/crash/content/tools/crash_service.h125
-rwxr-xr-xcomponents/crash/content/tools/dmp2minidump.py51
-rwxr-xr-xcomponents/crash/content/tools/generate_breakpad_symbols.py262
30 files changed, 6006 insertions, 0 deletions
diff --git a/components/crash/content/app/BUILD.gn b/components/crash/content/app/BUILD.gn
new file mode 100644
index 0000000..e0985c5
--- /dev/null
+++ b/components/crash/content/app/BUILD.gn
@@ -0,0 +1,79 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (is_android) {
+ import("//build/config/android/config.gni")
+}
+
+source_set("lib") {
+ sources = [
+ "crash_keys_win.cc",
+ "crash_keys_win.h",
+ "crash_reporter_client.cc",
+ "crash_reporter_client.h",
+ ]
+
+ include_dirs = [ "../../../../breakpad/src" ]
+}
+
+# Note: if you depend on this target, you need to either link in
+# content.gyp:content_common, or add content/public/common/content_switches.cc
+# to your sources.
+#
+# GYP version: components/crash.gypi:crash_component
+source_set("app") {
+ sources = [
+ "breakpad_linux_impl.h",
+ "breakpad_mac.h",
+ "breakpad_mac.mm",
+ "breakpad_win.cc",
+ "breakpad_win.h",
+ "hard_error_handler_win.cc",
+ "hard_error_handler_win.h",
+ ]
+
+ if (is_android) {
+ libs = [ "log" ]
+ }
+
+ if (is_android || is_linux) {
+ # Want these files on both Linux and Android.
+ set_sources_assignment_filter([])
+ sources += [
+ "breakpad_linux.cc",
+ "breakpad_linux.h",
+ ]
+ }
+
+ defines = [ "CRASH_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ "//base:base_static",
+ ":lib",
+ ]
+
+ if (is_android) {
+ defines += [ "CHROME_BUILD_ID=" + android_chrome_build_id ]
+ }
+
+ if (is_mac) {
+ deps += [ "//breakpad" ]
+ } else if (is_win) {
+ deps += [
+ "//sandbox",
+ "//breakpad:breakpad_handler",
+
+ #'../breakpad/breakpad.gyp:breakpad_sender', TODO(GYP)
+ ]
+ } else if (is_posix && !is_ios) {
+ deps += [ "//breakpad:client" ]
+ }
+}
+
+source_set("test_support") {
+ deps = [
+ ":lib",
+ ]
+}
diff --git a/components/crash/content/app/DEPS b/components/crash/content/app/DEPS
new file mode 100644
index 0000000..dc5e932
--- /dev/null
+++ b/components/crash/content/app/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+sandbox",
+
+ "+content/public/common/content_descriptors.h",
+ "+content/public/common/result_codes.h",
+ "+third_party/crashpad",
+ "+third_party/lss/linux_syscall_support.h",
+]
diff --git a/components/crash/content/app/breakpad_linux.cc b/components/crash/content/app/breakpad_linux.cc
new file mode 100644
index 0000000..c08a7f9
--- /dev/null
+++ b/components/crash/content/app/breakpad_linux.cc
@@ -0,0 +1,1760 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// For linux_syscall_support.h. This makes it safe to call embedded system
+// calls when in seccomp mode.
+
+#include "components/crash/content/app/breakpad_linux.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/files/file_path.h"
+#include "base/linux_util.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/global_descriptors.h"
+#include "base/process/memory.h"
+#include "base/strings/string_util.h"
+#include "breakpad/src/client/linux/crash_generation/crash_generation_client.h"
+#include "breakpad/src/client/linux/handler/exception_handler.h"
+#include "breakpad/src/client/linux/minidump_writer/directory_reader.h"
+#include "breakpad/src/common/linux/linux_libc_support.h"
+#include "breakpad/src/common/memory.h"
+#include "build/build_config.h"
+#include "components/crash/content/app/breakpad_linux_impl.h"
+#include "components/crash/content/app/crash_reporter_client.h"
+#include "content/public/common/content_descriptors.h"
+
+#if defined(OS_ANDROID)
+#include <android/log.h>
+#include <sys/stat.h>
+
+#include "base/android/build_info.h"
+#include "base/android/path_utils.h"
+#include "base/debug/leak_annotations.h"
+#endif
+#include "third_party/lss/linux_syscall_support.h"
+
+#if defined(ADDRESS_SANITIZER)
+#include <ucontext.h> // for getcontext().
+#endif
+
+#if defined(OS_ANDROID)
+#define STAT_STRUCT struct stat
+#define FSTAT_FUNC fstat
+#else
+#define STAT_STRUCT struct kernel_stat
+#define FSTAT_FUNC sys_fstat
+#endif
+
+// Some versions of gcc are prone to warn about unused return values. In cases
+// where we either a) know the call cannot fail, or b) there is nothing we
+// can do when a call fails, we mark the return code as ignored. This avoids
+// spurious compiler warnings.
+#define IGNORE_RET(x) do { if (x); } while (0)
+
+using crash_reporter::GetCrashReporterClient;
+using google_breakpad::ExceptionHandler;
+using google_breakpad::MinidumpDescriptor;
+
+namespace breakpad {
+
+namespace {
+
+#if !defined(OS_CHROMEOS)
+const char kUploadURL[] = "https://clients2.google.com/cr/report";
+#endif
+
+bool g_is_crash_reporter_enabled = false;
+uint64_t g_process_start_time = 0;
+pid_t g_pid = 0;
+char* g_crash_log_path = nullptr;
+ExceptionHandler* g_breakpad = nullptr;
+
+#if defined(ADDRESS_SANITIZER)
+const char* g_asan_report_str = nullptr;
+#endif
+#if defined(OS_ANDROID)
+char* g_process_type = nullptr;
+ExceptionHandler* g_microdump = nullptr;
+const char* g_microdump_build_fingerprint = nullptr;
+const char* g_microdump_product_info = nullptr;
+#endif
+
+CrashKeyStorage* g_crash_keys = nullptr;
+
+// Writes the value |v| as 16 hex characters to the memory pointed at by
+// |output|.
+void write_uint64_hex(char* output, uint64_t v) {
+ static const char hextable[] = "0123456789abcdef";
+
+ for (int i = 15; i >= 0; --i) {
+ output[i] = hextable[v & 15];
+ v >>= 4;
+ }
+}
+
+// The following helper functions are for calculating uptime.
+
+// Converts a struct timeval to milliseconds.
+uint64_t timeval_to_ms(struct timeval *tv) {
+ uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t.
+ ret *= 1000;
+ ret += tv->tv_usec / 1000;
+ return ret;
+}
+
+// Converts a struct timeval to milliseconds.
+uint64_t kernel_timeval_to_ms(struct kernel_timeval *tv) {
+ uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t.
+ ret *= 1000;
+ ret += tv->tv_usec / 1000;
+ return ret;
+}
+
+// String buffer size to use to convert a uint64_t to string.
+const size_t kUint64StringSize = 21;
+
+void SetProcessStartTime() {
+ // Set the base process start time value.
+ struct timeval tv;
+ if (!gettimeofday(&tv, nullptr))
+ g_process_start_time = timeval_to_ms(&tv);
+ else
+ g_process_start_time = 0;
+}
+
+// uint64_t version of my_int_len() from
+// breakpad/src/common/linux/linux_libc_support.h. Return the length of the
+// given, non-negative integer when expressed in base 10.
+unsigned my_uint64_len(uint64_t i) {
+ if (!i)
+ return 1;
+
+ unsigned len = 0;
+ while (i) {
+ len++;
+ i /= 10;
+ }
+
+ return len;
+}
+
+// uint64_t version of my_uitos() from
+// breakpad/src/common/linux/linux_libc_support.h. Convert a non-negative
+// integer to a string (not null-terminated).
+void my_uint64tos(char* output, uint64_t i, unsigned i_len) {
+ for (unsigned index = i_len; index; --index, i /= 10)
+ output[index - 1] = '0' + (i % 10);
+}
+
+#if !defined(OS_CHROMEOS)
+bool my_isxdigit(char c) {
+ return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f');
+}
+#endif
+
+size_t LengthWithoutTrailingSpaces(const char* str, size_t len) {
+ while (len > 0 && str[len - 1] == ' ') {
+ len--;
+ }
+ return len;
+}
+
+void SetClientIdFromCommandLine(const base::CommandLine& command_line) {
+ // Get the guid from the command line switch.
+ std::string switch_value =
+ command_line.GetSwitchValueASCII(switches::kEnableCrashReporter);
+ GetCrashReporterClient()->SetCrashReporterClientIdFromGUID(switch_value);
+}
+
+// MIME substrings.
+#if defined(OS_CHROMEOS)
+const char g_sep[] = ":";
+#endif
+const char g_rn[] = "\r\n";
+const char g_form_data_msg[] = "Content-Disposition: form-data; name=\"";
+const char g_quote_msg[] = "\"";
+const char g_dashdash_msg[] = "--";
+const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\"";
+#if defined(ADDRESS_SANITIZER)
+const char g_log_msg[] = "upload_file_log\"; filename=\"log\"";
+#endif
+const char g_content_type_msg[] = "Content-Type: application/octet-stream";
+
+// MimeWriter manages an iovec for writing MIMEs to a file.
+class MimeWriter {
+ public:
+ static const int kIovCapacity = 30;
+ static const size_t kMaxCrashChunkSize = 64;
+
+ MimeWriter(int fd, const char* const mime_boundary);
+ ~MimeWriter();
+
+ // Append boundary.
+ virtual void AddBoundary();
+
+ // Append end of file boundary.
+ virtual void AddEnd();
+
+ // Append key/value pair with specified sizes.
+ virtual void AddPairData(const char* msg_type,
+ size_t msg_type_size,
+ const char* msg_data,
+ size_t msg_data_size);
+
+ // Append key/value pair.
+ void AddPairString(const char* msg_type,
+ const char* msg_data) {
+ AddPairData(msg_type, my_strlen(msg_type), msg_data, my_strlen(msg_data));
+ }
+
+ // Append key/value pair, splitting value into chunks no larger than
+ // |chunk_size|. |chunk_size| cannot be greater than |kMaxCrashChunkSize|.
+ // The msg_type string will have a counter suffix to distinguish each chunk.
+ virtual void AddPairDataInChunks(const char* msg_type,
+ size_t msg_type_size,
+ const char* msg_data,
+ size_t msg_data_size,
+ size_t chunk_size,
+ bool strip_trailing_spaces);
+
+ // Add binary file contents to be uploaded with the specified filename.
+ virtual void AddFileContents(const char* filename_msg,
+ uint8_t* file_data,
+ size_t file_size);
+
+ // Flush any pending iovecs to the output file.
+ void Flush() {
+ IGNORE_RET(sys_writev(fd_, iov_, iov_index_));
+ iov_index_ = 0;
+ }
+
+ protected:
+ void AddItem(const void* base, size_t size);
+ // Minor performance trade-off for easier-to-maintain code.
+ void AddString(const char* str) {
+ AddItem(str, my_strlen(str));
+ }
+ void AddItemWithoutTrailingSpaces(const void* base, size_t size);
+
+ struct kernel_iovec iov_[kIovCapacity];
+ int iov_index_;
+
+ // Output file descriptor.
+ int fd_;
+
+ const char* const mime_boundary_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MimeWriter);
+};
+
+MimeWriter::MimeWriter(int fd, const char* const mime_boundary)
+ : iov_index_(0),
+ fd_(fd),
+ mime_boundary_(mime_boundary) {
+}
+
+MimeWriter::~MimeWriter() {
+}
+
+void MimeWriter::AddBoundary() {
+ AddString(mime_boundary_);
+ AddString(g_rn);
+}
+
+void MimeWriter::AddEnd() {
+ AddString(mime_boundary_);
+ AddString(g_dashdash_msg);
+ AddString(g_rn);
+}
+
+void MimeWriter::AddPairData(const char* msg_type,
+ size_t msg_type_size,
+ const char* msg_data,
+ size_t msg_data_size) {
+ AddString(g_form_data_msg);
+ AddItem(msg_type, msg_type_size);
+ AddString(g_quote_msg);
+ AddString(g_rn);
+ AddString(g_rn);
+ AddItem(msg_data, msg_data_size);
+ AddString(g_rn);
+}
+
+void MimeWriter::AddPairDataInChunks(const char* msg_type,
+ size_t msg_type_size,
+ const char* msg_data,
+ size_t msg_data_size,
+ size_t chunk_size,
+ bool strip_trailing_spaces) {
+ if (chunk_size > kMaxCrashChunkSize)
+ return;
+
+ unsigned i = 0;
+ size_t done = 0, msg_length = msg_data_size;
+
+ while (msg_length) {
+ char num[kUint64StringSize];
+ const unsigned num_len = my_uint_len(++i);
+ my_uitos(num, i, num_len);
+
+ size_t chunk_len = std::min(chunk_size, msg_length);
+
+ AddString(g_form_data_msg);
+ AddItem(msg_type, msg_type_size);
+ AddItem(num, num_len);
+ AddString(g_quote_msg);
+ AddString(g_rn);
+ AddString(g_rn);
+ if (strip_trailing_spaces) {
+ AddItemWithoutTrailingSpaces(msg_data + done, chunk_len);
+ } else {
+ AddItem(msg_data + done, chunk_len);
+ }
+ AddString(g_rn);
+ AddBoundary();
+ Flush();
+
+ done += chunk_len;
+ msg_length -= chunk_len;
+ }
+}
+
+void MimeWriter::AddFileContents(const char* filename_msg, uint8_t* file_data,
+ size_t file_size) {
+ AddString(g_form_data_msg);
+ AddString(filename_msg);
+ AddString(g_rn);
+ AddString(g_content_type_msg);
+ AddString(g_rn);
+ AddString(g_rn);
+ AddItem(file_data, file_size);
+ AddString(g_rn);
+}
+
+void MimeWriter::AddItem(const void* base, size_t size) {
+ // Check if the iovec is full and needs to be flushed to output file.
+ if (iov_index_ == kIovCapacity) {
+ Flush();
+ }
+ iov_[iov_index_].iov_base = const_cast<void*>(base);
+ iov_[iov_index_].iov_len = size;
+ ++iov_index_;
+}
+
+void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) {
+ AddItem(base, LengthWithoutTrailingSpaces(static_cast<const char*>(base),
+ size));
+}
+
+#if defined(OS_CHROMEOS)
+// This subclass is used on Chromium OS to report crashes in a format easy for
+// the central crash reporting facility to understand.
+// Format is <name>:<data length in decimal>:<data>
+class CrashReporterWriter : public MimeWriter {
+ public:
+ explicit CrashReporterWriter(int fd);
+
+ void AddBoundary() override;
+
+ void AddEnd() override;
+
+ void AddPairData(const char* msg_type,
+ size_t msg_type_size,
+ const char* msg_data,
+ size_t msg_data_size) override;
+
+ void AddPairDataInChunks(const char* msg_type,
+ size_t msg_type_size,
+ const char* msg_data,
+ size_t msg_data_size,
+ size_t chunk_size,
+ bool strip_trailing_spaces) override;
+
+ void AddFileContents(const char* filename_msg,
+ uint8_t* file_data,
+ size_t file_size) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CrashReporterWriter);
+};
+
+
+CrashReporterWriter::CrashReporterWriter(int fd) : MimeWriter(fd, "") {}
+
+// No-ops.
+void CrashReporterWriter::AddBoundary() {}
+void CrashReporterWriter::AddEnd() {}
+
+void CrashReporterWriter::AddPairData(const char* msg_type,
+ size_t msg_type_size,
+ const char* msg_data,
+ size_t msg_data_size) {
+ char data[kUint64StringSize];
+ const unsigned data_len = my_uint_len(msg_data_size);
+ my_uitos(data, msg_data_size, data_len);
+
+ AddItem(msg_type, msg_type_size);
+ AddString(g_sep);
+ AddItem(data, data_len);
+ AddString(g_sep);
+ AddItem(msg_data, msg_data_size);
+ Flush();
+}
+
+void CrashReporterWriter::AddPairDataInChunks(const char* msg_type,
+ size_t msg_type_size,
+ const char* msg_data,
+ size_t msg_data_size,
+ size_t chunk_size,
+ bool strip_trailing_spaces) {
+ if (chunk_size > kMaxCrashChunkSize)
+ return;
+
+ unsigned i = 0;
+ size_t done = 0;
+ size_t msg_length = msg_data_size;
+
+ while (msg_length) {
+ char num[kUint64StringSize];
+ const unsigned num_len = my_uint_len(++i);
+ my_uitos(num, i, num_len);
+
+ size_t chunk_len = std::min(chunk_size, msg_length);
+
+ size_t write_len = chunk_len;
+ if (strip_trailing_spaces) {
+ // Take care of this here because we need to know the exact length of
+ // what is going to be written.
+ write_len = LengthWithoutTrailingSpaces(msg_data + done, write_len);
+ }
+
+ char data[kUint64StringSize];
+ const unsigned data_len = my_uint_len(write_len);
+ my_uitos(data, write_len, data_len);
+
+ AddItem(msg_type, msg_type_size);
+ AddItem(num, num_len);
+ AddString(g_sep);
+ AddItem(data, data_len);
+ AddString(g_sep);
+ AddItem(msg_data + done, write_len);
+ Flush();
+
+ done += chunk_len;
+ msg_length -= chunk_len;
+ }
+}
+
+void CrashReporterWriter::AddFileContents(const char* filename_msg,
+ uint8_t* file_data,
+ size_t file_size) {
+ char data[kUint64StringSize];
+ const unsigned data_len = my_uint_len(file_size);
+ my_uitos(data, file_size, data_len);
+
+ AddString(filename_msg);
+ AddString(g_sep);
+ AddItem(data, data_len);
+ AddString(g_sep);
+ AddItem(file_data, file_size);
+ Flush();
+}
+#endif // defined(OS_CHROMEOS)
+
+void DumpProcess() {
+ if (g_breakpad)
+ g_breakpad->WriteMinidump();
+
+#if defined(OS_ANDROID)
+ // If microdumps are enabled write also a microdump on the system log.
+ if (g_microdump)
+ g_microdump->WriteMinidump();
+#endif
+}
+
+#if defined(OS_ANDROID)
+const char kGoogleBreakpad[] = "google-breakpad";
+#endif
+
+size_t WriteLog(const char* buf, size_t nbytes) {
+#if defined(OS_ANDROID)
+ return __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad, buf);
+#else
+ return sys_write(2, buf, nbytes);
+#endif
+}
+
+size_t WriteNewline() {
+ return WriteLog("\n", 1);
+}
+
+#if defined(OS_ANDROID)
+void AndroidLogWriteHorizontalRule() {
+ __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
+ "### ### ### ### ### ### ### ### ### ### ### ### ###");
+}
+
+// Android's native crash handler outputs a diagnostic tombstone to the device
+// log. By returning false from the HandlerCallbacks, breakpad will reinstall
+// the previous (i.e. native) signal handlers before returning from its own
+// handler. A Chrome build fingerprint is written to the log, so that the
+// specific build of Chrome and the location of the archived Chrome symbols can
+// be determined directly from it.
+bool FinalizeCrashDoneAndroid(bool is_browser_process) {
+ base::android::BuildInfo* android_build_info =
+ base::android::BuildInfo::GetInstance();
+
+ AndroidLogWriteHorizontalRule();
+ __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
+ "Chrome build fingerprint:");
+ __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
+ android_build_info->package_version_name());
+ __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
+ android_build_info->package_version_code());
+ __android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
+ CHROME_BUILD_ID);
+ AndroidLogWriteHorizontalRule();
+
+ if (!is_browser_process &&
+ android_build_info->sdk_int() >= 18 &&
+ my_strcmp(android_build_info->build_type(), "eng") != 0 &&
+ my_strcmp(android_build_info->build_type(), "userdebug") != 0) {
+ // On JB MR2 and later, the system crash handler displays a dialog. For
+ // renderer crashes, this is a bad user experience and so this is disabled
+ // for user builds of Android.
+ // TODO(cjhopman): There should be some way to recover the crash stack from
+ // non-uploading user clients. See http://crbug.com/273706.
+ __android_log_write(ANDROID_LOG_WARN,
+ kGoogleBreakpad,
+ "Tombstones are disabled on JB MR2+ user builds.");
+ AndroidLogWriteHorizontalRule();
+ return true;
+ }
+ return false;
+}
+#endif
+
+bool CrashDone(const MinidumpDescriptor& minidump,
+ const bool upload,
+ const bool succeeded) {
+ // WARNING: this code runs in a compromised context. It may not call into
+ // libc nor allocate memory normally.
+ if (!succeeded) {
+ const char msg[] = "Failed to generate minidump.";
+ WriteLog(msg, sizeof(msg) - 1);
+ return false;
+ }
+
+ DCHECK(!minidump.IsFD());
+
+ BreakpadInfo info = {0};
+ info.filename = minidump.path();
+ info.fd = minidump.fd();
+#if defined(ADDRESS_SANITIZER)
+ google_breakpad::PageAllocator allocator;
+ const size_t log_path_len = my_strlen(minidump.path());
+ char* log_path = reinterpret_cast<char*>(allocator.Alloc(log_path_len + 1));
+ my_memcpy(log_path, minidump.path(), log_path_len);
+ my_memcpy(log_path + log_path_len - 4, ".log", 4);
+ log_path[log_path_len] = '\0';
+ info.log_filename = log_path;
+#endif
+ info.process_type = "browser";
+ info.process_type_length = 7;
+ info.distro = base::g_linux_distro;
+ info.distro_length = my_strlen(base::g_linux_distro);
+ info.upload = upload;
+ info.process_start_time = g_process_start_time;
+ info.oom_size = base::g_oom_size;
+ info.pid = g_pid;
+ info.crash_keys = g_crash_keys;
+ HandleCrashDump(info);
+#if defined(OS_ANDROID)
+ return FinalizeCrashDoneAndroid(true /* is_browser_process */);
+#else
+ return true;
+#endif
+}
+
+// Wrapper function, do not add more code here.
+bool CrashDoneNoUpload(const MinidumpDescriptor& minidump,
+ void* context,
+ bool succeeded) {
+ return CrashDone(minidump, false, succeeded);
+}
+
+#if !defined(OS_ANDROID)
+// Wrapper function, do not add more code here.
+bool CrashDoneUpload(const MinidumpDescriptor& minidump,
+ void* context,
+ bool succeeded) {
+ return CrashDone(minidump, true, succeeded);
+}
+#endif
+
+#if defined(ADDRESS_SANITIZER)
+extern "C"
+void __asan_set_error_report_callback(void (*cb)(const char*));
+
+extern "C"
+void AsanLinuxBreakpadCallback(const char* report) {
+ g_asan_report_str = report;
+ // Send minidump here.
+ g_breakpad->SimulateSignalDelivery(SIGKILL);
+}
+#endif
+
+void EnableCrashDumping(bool unattended) {
+ g_is_crash_reporter_enabled = true;
+
+ base::FilePath tmp_path("/tmp");
+ PathService::Get(base::DIR_TEMP, &tmp_path);
+
+ base::FilePath dumps_path(tmp_path);
+ if (GetCrashReporterClient()->GetCrashDumpLocation(&dumps_path)) {
+ base::FilePath logfile =
+ dumps_path.Append(GetCrashReporterClient()->GetReporterLogFilename());
+ std::string logfile_str = logfile.value();
+ const size_t crash_log_path_len = logfile_str.size() + 1;
+ g_crash_log_path = new char[crash_log_path_len];
+ strncpy(g_crash_log_path, logfile_str.c_str(), crash_log_path_len);
+ }
+ DCHECK(!g_breakpad);
+ MinidumpDescriptor minidump_descriptor(dumps_path.value());
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kFullMemoryCrashReport)) {
+ minidump_descriptor.set_size_limit(-1); // unlimited.
+ } else {
+ minidump_descriptor.set_size_limit(kMaxMinidumpFileSize);
+ }
+#if defined(OS_ANDROID)
+ unattended = true; // Android never uploads directly.
+#endif
+ if (unattended) {
+ g_breakpad = new ExceptionHandler(
+ minidump_descriptor,
+ nullptr,
+ CrashDoneNoUpload,
+ nullptr,
+ true, // Install handlers.
+ -1); // Server file descriptor. -1 for in-process.
+ return;
+ }
+
+#if !defined(OS_ANDROID)
+ // Attended mode
+ g_breakpad = new ExceptionHandler(
+ minidump_descriptor,
+ nullptr,
+ CrashDoneUpload,
+ nullptr,
+ true, // Install handlers.
+ -1); // Server file descriptor. -1 for in-process.
+#endif
+}
+
+#if defined(OS_ANDROID)
+bool MicrodumpCrashDone(const MinidumpDescriptor& minidump,
+ void* context,
+ bool succeeded) {
+ // WARNING: this code runs in a compromised context. It may not call into
+ // libc nor allocate memory normally.
+ if (!succeeded) {
+ static const char msg[] = "Microdump crash handler failed.\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ return false;
+ }
+
+ const bool is_browser_process = (context != nullptr);
+ return FinalizeCrashDoneAndroid(is_browser_process);
+ }
+
+// The microdump handler does NOT upload anything. It just dumps out on the
+// system console (logcat) a restricted and serialized variant of a minidump.
+// See crbug.com/410294 for more details.
+void InitMicrodumpCrashHandlerIfNecessary(const std::string& process_type) {
+ if (!GetCrashReporterClient()->ShouldEnableBreakpadMicrodumps())
+ return;
+
+ VLOG(1) << "Enabling microdumps crash handler (process_type:"
+ << process_type << ")";
+
+ // The exception handler runs in a compromised context and cannot use c_str()
+ // as that would require the heap. Therefore, we have to guarantee that the
+ // build fingerprint and product info pointers are always valid.
+ const char* product_name = nullptr;
+ const char* product_version = nullptr;
+ GetCrashReporterClient()->GetProductNameAndVersion(&product_name,
+ &product_version);
+
+ MinidumpDescriptor descriptor(MinidumpDescriptor::kMicrodumpOnConsole);
+
+ if (product_name && product_version) {
+ g_microdump_product_info = strdup(
+ (product_name + std::string(":") + product_version).c_str());
+ ANNOTATE_LEAKING_OBJECT_PTR(g_microdump_product_info);
+ descriptor.SetMicrodumpProductInfo(g_microdump_product_info);
+ }
+
+ const char* android_build_fp =
+ base::android::BuildInfo::GetInstance()->android_build_fp();
+ if (android_build_fp) {
+ g_microdump_build_fingerprint = strdup(android_build_fp);
+ ANNOTATE_LEAKING_OBJECT_PTR(g_microdump_build_fingerprint);
+ descriptor.SetMicrodumpBuildFingerprint(g_microdump_build_fingerprint);
+ }
+
+ DCHECK(!g_microdump);
+ bool is_browser_process = process_type.empty() || process_type == "webview";
+ g_microdump = new ExceptionHandler(
+ descriptor,
+ nullptr,
+ MicrodumpCrashDone,
+ reinterpret_cast<void*>(is_browser_process),
+ true, // Install handlers.
+ -1); // Server file descriptor. -1 for in-process.
+ return;
+}
+
+bool CrashDoneInProcessNoUpload(
+ const google_breakpad::MinidumpDescriptor& descriptor,
+ void* context,
+ const bool succeeded) {
+ // WARNING: this code runs in a compromised context. It may not call into
+ // libc nor allocate memory normally.
+ if (!succeeded) {
+ static const char msg[] = "Crash dump generation failed.\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ return false;
+ }
+
+ // Start constructing the message to send to the browser.
+ BreakpadInfo info = {0};
+ info.filename = nullptr;
+ info.fd = descriptor.fd();
+ info.process_type = g_process_type;
+ info.process_type_length = my_strlen(g_process_type);
+ info.distro = nullptr;
+ info.distro_length = 0;
+ info.upload = false;
+ info.process_start_time = g_process_start_time;
+ info.pid = g_pid;
+ info.crash_keys = g_crash_keys;
+ HandleCrashDump(info);
+ return FinalizeCrashDoneAndroid(false /* is_browser_process */);
+}
+
+void EnableNonBrowserCrashDumping(const std::string& process_type,
+ int minidump_fd) {
+ // This will guarantee that the BuildInfo has been initialized and subsequent
+ // calls will not require memory allocation.
+ base::android::BuildInfo::GetInstance();
+ SetClientIdFromCommandLine(*base::CommandLine::ForCurrentProcess());
+
+ // On Android, the current sandboxing uses process isolation, in which the
+ // child process runs with a different UID. That breaks the normal crash
+ // reporting where the browser process generates the minidump by inspecting
+ // the child process. This is because the browser process now does not have
+ // the permission to access the states of the child process (as it has a
+ // different UID).
+ // TODO(jcivelli): http://b/issue?id=6776356 we should use a watchdog
+ // process forked from the renderer process that generates the minidump.
+ if (minidump_fd == -1) {
+ LOG(ERROR) << "Minidump file descriptor not found, crash reporting will "
+ " not work.";
+ return;
+ }
+ SetProcessStartTime();
+ g_pid = getpid();
+
+ g_is_crash_reporter_enabled = true;
+ // Save the process type (it is leaked).
+ const size_t process_type_len = process_type.size() + 1;
+ g_process_type = new char[process_type_len];
+ strncpy(g_process_type, process_type.c_str(), process_type_len);
+ new google_breakpad::ExceptionHandler(MinidumpDescriptor(minidump_fd),
+ nullptr, CrashDoneInProcessNoUpload, nullptr, true, -1);
+}
+#else
+// Non-Browser = Extension, Gpu, Plugins, Ppapi and Renderer
+class NonBrowserCrashHandler : public google_breakpad::CrashGenerationClient {
+ public:
+ NonBrowserCrashHandler()
+ : server_fd_(base::GlobalDescriptors::GetInstance()->Get(
+ kCrashDumpSignal)) {
+ }
+
+ ~NonBrowserCrashHandler() override {}
+
+ bool RequestDump(const void* crash_context,
+ size_t crash_context_size) override {
+ int fds[2] = { -1, -1 };
+ if (sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+ static const char msg[] = "Failed to create socket for crash dumping.\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ return false;
+ }
+
+ // Start constructing the message to send to the browser.
+ char b; // Dummy variable for sys_read below.
+ const char* b_addr = &b; // Get the address of |b| so we can create the
+ // expected /proc/[pid]/syscall content in the
+ // browser to convert namespace tids.
+
+ // The length of the control message:
+ static const unsigned kControlMsgSize = sizeof(int);
+ static const unsigned kControlMsgSpaceSize = CMSG_SPACE(kControlMsgSize);
+ static const unsigned kControlMsgLenSize = CMSG_LEN(kControlMsgSize);
+
+ struct kernel_msghdr msg;
+ my_memset(&msg, 0, sizeof(struct kernel_msghdr));
+ struct kernel_iovec iov[kCrashIovSize];
+ iov[0].iov_base = const_cast<void*>(crash_context);
+ iov[0].iov_len = crash_context_size;
+ iov[1].iov_base = &b_addr;
+ iov[1].iov_len = sizeof(b_addr);
+ iov[2].iov_base = &fds[0];
+ iov[2].iov_len = sizeof(fds[0]);
+ iov[3].iov_base = &g_process_start_time;
+ iov[3].iov_len = sizeof(g_process_start_time);
+ iov[4].iov_base = &base::g_oom_size;
+ iov[4].iov_len = sizeof(base::g_oom_size);
+ google_breakpad::SerializedNonAllocatingMap* serialized_map;
+ iov[5].iov_len = g_crash_keys->Serialize(
+ const_cast<const google_breakpad::SerializedNonAllocatingMap**>(
+ &serialized_map));
+ iov[5].iov_base = serialized_map;
+#if !defined(ADDRESS_SANITIZER)
+ static_assert(5 == kCrashIovSize - 1, "kCrashIovSize should equal 6");
+#else
+ iov[6].iov_base = const_cast<char*>(g_asan_report_str);
+ iov[6].iov_len = kMaxAsanReportSize + 1;
+ static_assert(6 == kCrashIovSize - 1, "kCrashIovSize should equal 7");
+#endif
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = kCrashIovSize;
+ char cmsg[kControlMsgSpaceSize];
+ my_memset(cmsg, 0, kControlMsgSpaceSize);
+ msg.msg_control = cmsg;
+ msg.msg_controllen = sizeof(cmsg);
+
+ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_SOCKET;
+ hdr->cmsg_type = SCM_RIGHTS;
+ hdr->cmsg_len = kControlMsgLenSize;
+ ((int*)CMSG_DATA(hdr))[0] = fds[1];
+
+ if (HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)) < 0) {
+ static const char errmsg[] = "Failed to tell parent about crash.\n";
+ WriteLog(errmsg, sizeof(errmsg) - 1);
+ IGNORE_RET(sys_close(fds[0]));
+ IGNORE_RET(sys_close(fds[1]));
+ return false;
+ }
+ IGNORE_RET(sys_close(fds[1]));
+
+ if (HANDLE_EINTR(sys_read(fds[0], &b, 1)) != 1) {
+ static const char errmsg[] = "Parent failed to complete crash dump.\n";
+ WriteLog(errmsg, sizeof(errmsg) - 1);
+ }
+ IGNORE_RET(sys_close(fds[0]));
+
+ return true;
+ }
+
+ private:
+ // The pipe FD to the browser process, which will handle the crash dumping.
+ const int server_fd_;
+
+ DISALLOW_COPY_AND_ASSIGN(NonBrowserCrashHandler);
+};
+
+void EnableNonBrowserCrashDumping() {
+ g_is_crash_reporter_enabled = true;
+ // We deliberately leak this object.
+ DCHECK(!g_breakpad);
+
+ g_breakpad = new ExceptionHandler(
+ MinidumpDescriptor("/tmp"), // Unused but needed or Breakpad will assert.
+ nullptr,
+ nullptr,
+ nullptr,
+ true,
+ -1);
+ g_breakpad->set_crash_generation_client(new NonBrowserCrashHandler());
+}
+#endif // defined(OS_ANDROID)
+
+void SetCrashKeyValue(const base::StringPiece& key,
+ const base::StringPiece& value) {
+ g_crash_keys->SetKeyValue(key.data(), value.data());
+}
+
+void ClearCrashKey(const base::StringPiece& key) {
+ g_crash_keys->RemoveKey(key.data());
+}
+
+// GetCrashReporterClient() cannot call any Set methods until after
+// InitCrashKeys().
+void InitCrashKeys() {
+ g_crash_keys = new CrashKeyStorage;
+ GetCrashReporterClient()->RegisterCrashKeys();
+ base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValue, &ClearCrashKey);
+}
+
+// Miscellaneous initialization functions to call after Breakpad has been
+// enabled.
+void PostEnableBreakpadInitialization() {
+ SetProcessStartTime();
+ g_pid = getpid();
+
+ base::debug::SetDumpWithoutCrashingFunction(&DumpProcess);
+#if defined(ADDRESS_SANITIZER)
+ // Register the callback for AddressSanitizer error reporting.
+ __asan_set_error_report_callback(AsanLinuxBreakpadCallback);
+#endif
+}
+
+} // namespace
+
+void LoadDataFromFD(google_breakpad::PageAllocator& allocator,
+ int fd, bool close_fd, uint8_t** file_data, size_t* size) {
+ STAT_STRUCT st;
+ if (FSTAT_FUNC(fd, &st) != 0) {
+ static const char msg[] = "Cannot upload crash dump: stat failed\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ if (close_fd)
+ IGNORE_RET(sys_close(fd));
+ return;
+ }
+
+ *file_data = reinterpret_cast<uint8_t*>(allocator.Alloc(st.st_size));
+ if (!(*file_data)) {
+ static const char msg[] = "Cannot upload crash dump: cannot alloc\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ if (close_fd)
+ IGNORE_RET(sys_close(fd));
+ return;
+ }
+ my_memset(*file_data, 0xf, st.st_size);
+
+ *size = st.st_size;
+ int byte_read = sys_read(fd, *file_data, *size);
+ if (byte_read == -1) {
+ static const char msg[] = "Cannot upload crash dump: read failed\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ if (close_fd)
+ IGNORE_RET(sys_close(fd));
+ return;
+ }
+
+ if (close_fd)
+ IGNORE_RET(sys_close(fd));
+}
+
+void LoadDataFromFile(google_breakpad::PageAllocator& allocator,
+ const char* filename,
+ int* fd, uint8_t** file_data, size_t* size) {
+ // WARNING: this code runs in a compromised context. It may not call into
+ // libc nor allocate memory normally.
+ *fd = sys_open(filename, O_RDONLY, 0);
+ *size = 0;
+
+ if (*fd < 0) {
+ static const char msg[] = "Cannot upload crash dump: failed to open\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ return;
+ }
+
+ LoadDataFromFD(allocator, *fd, true, file_data, size);
+}
+
+// Spawn the appropriate upload process for the current OS:
+// - generic Linux invokes wget.
+// - ChromeOS invokes crash_reporter.
+// |dumpfile| is the path to the dump data file.
+// |mime_boundary| is only used on Linux.
+// |exe_buf| is only used on CrOS and is the crashing process' name.
+void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
+ const char* dumpfile,
+ const char* mime_boundary,
+ const char* exe_buf,
+ google_breakpad::PageAllocator* allocator) {
+#if defined(OS_CHROMEOS)
+ // CrOS uses crash_reporter instead of wget to report crashes,
+ // it needs to know where the crash dump lives and the pid and uid of the
+ // crashing process.
+ static const char kCrashReporterBinary[] = "/sbin/crash_reporter";
+
+ char pid_buf[kUint64StringSize];
+ uint64_t pid_str_length = my_uint64_len(info.pid);
+ my_uint64tos(pid_buf, info.pid, pid_str_length);
+ pid_buf[pid_str_length] = '\0';
+
+ char uid_buf[kUint64StringSize];
+ uid_t uid = geteuid();
+ uint64_t uid_str_length = my_uint64_len(uid);
+ my_uint64tos(uid_buf, uid, uid_str_length);
+ uid_buf[uid_str_length] = '\0';
+
+ const char kChromeFlag[] = "--chrome=";
+ size_t buf_len = my_strlen(dumpfile) + sizeof(kChromeFlag);
+ char* chrome_flag = reinterpret_cast<char*>(allocator->Alloc(buf_len));
+ chrome_flag[0] = '\0';
+ my_strlcat(chrome_flag, kChromeFlag, buf_len);
+ my_strlcat(chrome_flag, dumpfile, buf_len);
+
+ const char kPidFlag[] = "--pid=";
+ buf_len = my_strlen(pid_buf) + sizeof(kPidFlag);
+ char* pid_flag = reinterpret_cast<char*>(allocator->Alloc(buf_len));
+ pid_flag[0] = '\0';
+ my_strlcat(pid_flag, kPidFlag, buf_len);
+ my_strlcat(pid_flag, pid_buf, buf_len);
+
+ const char kUidFlag[] = "--uid=";
+ buf_len = my_strlen(uid_buf) + sizeof(kUidFlag);
+ char* uid_flag = reinterpret_cast<char*>(allocator->Alloc(buf_len));
+ uid_flag[0] = '\0';
+ my_strlcat(uid_flag, kUidFlag, buf_len);
+ my_strlcat(uid_flag, uid_buf, buf_len);
+
+ const char kExeBuf[] = "--exe=";
+ buf_len = my_strlen(exe_buf) + sizeof(kExeBuf);
+ char* exe_flag = reinterpret_cast<char*>(allocator->Alloc(buf_len));
+ exe_flag[0] = '\0';
+ my_strlcat(exe_flag, kExeBuf, buf_len);
+ my_strlcat(exe_flag, exe_buf, buf_len);
+
+ const char* args[] = {
+ kCrashReporterBinary,
+ chrome_flag,
+ pid_flag,
+ uid_flag,
+ exe_flag,
+ nullptr,
+ };
+ static const char msg[] = "Cannot upload crash dump: cannot exec "
+ "/sbin/crash_reporter\n";
+#else
+ // Compress |dumpfile| with gzip.
+ const pid_t gzip_child = sys_fork();
+ if (gzip_child < 0) {
+ static const char msg[] = "sys_fork() for gzip process failed.\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ sys__exit(1);
+ }
+ if (!gzip_child) {
+ // gzip process.
+ const char* args[] = {
+ "/bin/gzip",
+ "-f", // Do not prompt to verify before overwriting.
+ dumpfile,
+ nullptr,
+ };
+ execve(args[0], const_cast<char**>(args), environ);
+ static const char msg[] = "Cannot exec gzip.\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ sys__exit(1);
+ }
+ // Wait for gzip process.
+ int status = 0;
+ if (sys_waitpid(gzip_child, &status, 0) != gzip_child ||
+ !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ static const char msg[] = "sys_waitpid() for gzip process failed.\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ sys_kill(gzip_child, SIGKILL);
+ sys__exit(1);
+ }
+
+ static const char kGzipExtension[] = ".gz";
+ const size_t gzip_file_size = my_strlen(dumpfile) + sizeof(kGzipExtension);
+ char* const gzip_file = reinterpret_cast<char*>(allocator->Alloc(
+ gzip_file_size));
+ my_strlcpy(gzip_file, dumpfile, gzip_file_size);
+ my_strlcat(gzip_file, kGzipExtension, gzip_file_size);
+
+ // Rename |gzip_file| to |dumpfile| (the original file was deleted by gzip).
+ if (rename(gzip_file, dumpfile)) {
+ static const char msg[] = "Failed to rename gzipped file.\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ sys__exit(1);
+ }
+
+ // The --header argument to wget looks like:
+ // --header=Content-Encoding: gzip
+ // --header=Content-Type: multipart/form-data; boundary=XYZ
+ // where the boundary has two fewer leading '-' chars
+ static const char header_content_encoding[] =
+ "--header=Content-Encoding: gzip";
+ static const char header_msg[] =
+ "--header=Content-Type: multipart/form-data; boundary=";
+ const size_t header_content_type_size =
+ sizeof(header_msg) - 1 + my_strlen(mime_boundary) - 2 + 1;
+ char* const header_content_type = reinterpret_cast<char*>(allocator->Alloc(
+ header_content_type_size));
+ my_strlcpy(header_content_type, header_msg, header_content_type_size);
+ my_strlcat(header_content_type, mime_boundary + 2, header_content_type_size);
+
+ // The --post-file argument to wget looks like:
+ // --post-file=/tmp/...
+ static const char post_file_msg[] = "--post-file=";
+ const size_t post_file_size =
+ sizeof(post_file_msg) - 1 + my_strlen(dumpfile) + 1;
+ char* const post_file = reinterpret_cast<char*>(allocator->Alloc(
+ post_file_size));
+ my_strlcpy(post_file, post_file_msg, post_file_size);
+ my_strlcat(post_file, dumpfile, post_file_size);
+
+ static const char kWgetBinary[] = "/usr/bin/wget";
+ const char* args[] = {
+ kWgetBinary,
+ header_content_encoding,
+ header_content_type,
+ post_file,
+ kUploadURL,
+ "--timeout=10", // Set a timeout so we don't hang forever.
+ "--tries=1", // Don't retry if the upload fails.
+ "-O", // output reply to fd 3
+ "/dev/fd/3",
+ nullptr,
+ };
+ static const char msg[] = "Cannot upload crash dump: cannot exec "
+ "/usr/bin/wget\n";
+#endif
+ execve(args[0], const_cast<char**>(args), environ);
+ WriteLog(msg, sizeof(msg) - 1);
+ sys__exit(1);
+}
+
+// Runs in the helper process to wait for the upload process running
+// ExecUploadProcessOrTerminate() to finish. Returns the number of bytes written
+// to |fd| and save the written contents to |buf|.
+// |buf| needs to be big enough to hold |bytes_to_read| + 1 characters.
+size_t WaitForCrashReportUploadProcess(int fd, size_t bytes_to_read,
+ char* buf) {
+ size_t bytes_read = 0;
+
+ // Upload should finish in about 10 seconds. Add a few more 500 ms
+ // internals to account for process startup time.
+ for (size_t wait_count = 0; wait_count < 24; ++wait_count) {
+ struct kernel_pollfd poll_fd;
+ poll_fd.fd = fd;
+ poll_fd.events = POLLIN | POLLPRI | POLLERR;
+ int ret = sys_poll(&poll_fd, 1, 500);
+ if (ret < 0) {
+ // Error
+ break;
+ } else if (ret > 0) {
+ // There is data to read.
+ ssize_t len = HANDLE_EINTR(
+ sys_read(fd, buf + bytes_read, bytes_to_read - bytes_read));
+ if (len < 0)
+ break;
+ bytes_read += len;
+ if (bytes_read == bytes_to_read)
+ break;
+ }
+ // |ret| == 0 -> timed out, continue waiting.
+ // or |bytes_read| < |bytes_to_read| still, keep reading.
+ }
+ buf[bytes_to_read] = 0; // Always NUL terminate the buffer.
+ return bytes_read;
+}
+
+// |buf| should be |expected_len| + 1 characters in size and nullptr terminated.
+bool IsValidCrashReportId(const char* buf, size_t bytes_read,
+ size_t expected_len) {
+ if (bytes_read != expected_len)
+ return false;
+#if defined(OS_CHROMEOS)
+ return my_strcmp(buf, "_sys_cr_finished") == 0;
+#else
+ for (size_t i = 0; i < bytes_read; ++i) {
+ if (!my_isxdigit(buf[i]))
+ return false;
+ }
+ return true;
+#endif
+}
+
+// |buf| should be |expected_len| + 1 characters in size and nullptr terminated.
+void HandleCrashReportId(const char* buf, size_t bytes_read,
+ size_t expected_len) {
+ WriteNewline();
+ if (!IsValidCrashReportId(buf, bytes_read, expected_len)) {
+#if defined(OS_CHROMEOS)
+ static const char msg[] =
+ "System crash-reporter failed to process crash report.";
+#else
+ static const char msg[] = "Failed to get crash dump id.";
+#endif
+ WriteLog(msg, sizeof(msg) - 1);
+ WriteNewline();
+
+ static const char id_msg[] = "Report Id: ";
+ WriteLog(id_msg, sizeof(id_msg) - 1);
+ WriteLog(buf, bytes_read);
+ WriteNewline();
+ return;
+ }
+
+#if defined(OS_CHROMEOS)
+ static const char msg[] = "Crash dump received by crash_reporter\n";
+ WriteLog(msg, sizeof(msg) - 1);
+#else
+ // Write crash dump id to stderr.
+ static const char msg[] = "Crash dump id: ";
+ WriteLog(msg, sizeof(msg) - 1);
+ WriteLog(buf, my_strlen(buf));
+ WriteNewline();
+
+ // Write crash dump id to crash log as: seconds_since_epoch,crash_id
+ struct kernel_timeval tv;
+ if (g_crash_log_path && !sys_gettimeofday(&tv, nullptr)) {
+ uint64_t time = kernel_timeval_to_ms(&tv) / 1000;
+ char time_str[kUint64StringSize];
+ const unsigned time_len = my_uint64_len(time);
+ my_uint64tos(time_str, time, time_len);
+
+ const int kLogOpenFlags = O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC;
+ int log_fd = sys_open(g_crash_log_path, kLogOpenFlags, 0600);
+ if (log_fd > 0) {
+ sys_write(log_fd, time_str, time_len);
+ sys_write(log_fd, ",", 1);
+ sys_write(log_fd, buf, my_strlen(buf));
+ sys_write(log_fd, "\n", 1);
+ IGNORE_RET(sys_close(log_fd));
+ }
+ }
+#endif
+}
+
+#if defined(OS_CHROMEOS)
+const char* GetCrashingProcessName(const BreakpadInfo& info,
+ google_breakpad::PageAllocator* allocator) {
+ // Symlink to process binary is at /proc/###/exe.
+ char linkpath[kUint64StringSize + sizeof("/proc/") + sizeof("/exe")] =
+ "/proc/";
+ uint64_t pid_value_len = my_uint64_len(info.pid);
+ my_uint64tos(linkpath + sizeof("/proc/") - 1, info.pid, pid_value_len);
+ linkpath[sizeof("/proc/") - 1 + pid_value_len] = '\0';
+ my_strlcat(linkpath, "/exe", sizeof(linkpath));
+
+ const int kMaxSize = 4096;
+ char* link = reinterpret_cast<char*>(allocator->Alloc(kMaxSize));
+ if (link) {
+ ssize_t size = readlink(linkpath, link, kMaxSize);
+ if (size < kMaxSize && size > 0) {
+ // readlink(2) doesn't add a terminating NUL, so do it now.
+ link[size] = '\0';
+
+ const char* name = my_strrchr(link, '/');
+ if (name)
+ return name + 1;
+ return link;
+ }
+ }
+ // Either way too long, or a read error.
+ return "chrome-crash-unknown-process";
+}
+#endif
+
+void HandleCrashDump(const BreakpadInfo& info) {
+ int dumpfd;
+ bool keep_fd = false;
+ size_t dump_size;
+ uint8_t* dump_data;
+ google_breakpad::PageAllocator allocator;
+ const char* exe_buf = nullptr;
+
+ if (GetCrashReporterClient()->HandleCrashDump(info.filename)) {
+ return;
+ }
+
+#if defined(OS_CHROMEOS)
+ // Grab the crashing process' name now, when it should still be available.
+ // If we try to do this later in our grandchild the crashing process has
+ // already terminated.
+ exe_buf = GetCrashingProcessName(info, &allocator);
+#endif
+
+ if (info.fd != -1) {
+ // Dump is provided with an open FD.
+ keep_fd = true;
+ dumpfd = info.fd;
+
+ // The FD is pointing to the end of the file.
+ // Rewind, we'll read the data next.
+ if (lseek(dumpfd, 0, SEEK_SET) == -1) {
+ static const char msg[] = "Cannot upload crash dump: failed to "
+ "reposition minidump FD\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ IGNORE_RET(sys_close(dumpfd));
+ return;
+ }
+ LoadDataFromFD(allocator, info.fd, false, &dump_data, &dump_size);
+ } else {
+ // Dump is provided with a path.
+ keep_fd = false;
+ LoadDataFromFile(allocator, info.filename, &dumpfd, &dump_data, &dump_size);
+ }
+
+ // TODO(jcivelli): make log work when using FDs.
+#if defined(ADDRESS_SANITIZER)
+ int logfd;
+ size_t log_size;
+ uint8_t* log_data;
+ // Load the AddressSanitizer log into log_data.
+ LoadDataFromFile(allocator, info.log_filename, &logfd, &log_data, &log_size);
+#endif
+
+ // We need to build a MIME block for uploading to the server. Since we are
+ // going to fork and run wget, it needs to be written to a temp file.
+ const int ufd = sys_open("/dev/urandom", O_RDONLY, 0);
+ if (ufd < 0) {
+ static const char msg[] = "Cannot upload crash dump because /dev/urandom"
+ " is missing\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ return;
+ }
+
+ static const char temp_file_template[] =
+ "/tmp/chromium-upload-XXXXXXXXXXXXXXXX";
+ char temp_file[sizeof(temp_file_template)];
+ int temp_file_fd = -1;
+ if (keep_fd) {
+ temp_file_fd = dumpfd;
+ // Rewind the destination, we are going to overwrite it.
+ if (lseek(dumpfd, 0, SEEK_SET) == -1) {
+ static const char msg[] = "Cannot upload crash dump: failed to "
+ "reposition minidump FD (2)\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ IGNORE_RET(sys_close(dumpfd));
+ return;
+ }
+ } else {
+ if (info.upload) {
+ my_memcpy(temp_file, temp_file_template, sizeof(temp_file_template));
+
+ for (unsigned i = 0; i < 10; ++i) {
+ uint64_t t;
+ sys_read(ufd, &t, sizeof(t));
+ write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t);
+
+ temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (temp_file_fd >= 0)
+ break;
+ }
+
+ if (temp_file_fd < 0) {
+ static const char msg[] = "Failed to create temporary file in /tmp: "
+ "cannot upload crash dump\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ IGNORE_RET(sys_close(ufd));
+ return;
+ }
+ } else {
+ temp_file_fd = sys_open(info.filename, O_WRONLY, 0600);
+ if (temp_file_fd < 0) {
+ static const char msg[] = "Failed to save crash dump: failed to open\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ IGNORE_RET(sys_close(ufd));
+ return;
+ }
+ }
+ }
+
+ // The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL.
+ char mime_boundary[28 + 16 + 1];
+ my_memset(mime_boundary, '-', 28);
+ uint64_t boundary_rand;
+ sys_read(ufd, &boundary_rand, sizeof(boundary_rand));
+ write_uint64_hex(mime_boundary + 28, boundary_rand);
+ mime_boundary[28 + 16] = 0;
+ IGNORE_RET(sys_close(ufd));
+
+ // The MIME block looks like this:
+ // BOUNDARY \r\n
+ // Content-Disposition: form-data; name="prod" \r\n \r\n
+ // Chrome_Linux \r\n
+ // BOUNDARY \r\n
+ // Content-Disposition: form-data; name="ver" \r\n \r\n
+ // 1.2.3.4 \r\n
+ // BOUNDARY \r\n
+ //
+ // zero or one:
+ // Content-Disposition: form-data; name="ptime" \r\n \r\n
+ // abcdef \r\n
+ // BOUNDARY \r\n
+ //
+ // zero or one:
+ // Content-Disposition: form-data; name="ptype" \r\n \r\n
+ // abcdef \r\n
+ // BOUNDARY \r\n
+ //
+ // zero or one:
+ // Content-Disposition: form-data; name="lsb-release" \r\n \r\n
+ // abcdef \r\n
+ // BOUNDARY \r\n
+ //
+ // zero or one:
+ // Content-Disposition: form-data; name="oom-size" \r\n \r\n
+ // 1234567890 \r\n
+ // BOUNDARY \r\n
+ //
+ // zero or more (up to CrashKeyStorage::num_entries = 64):
+ // Content-Disposition: form-data; name=crash-key-name \r\n
+ // crash-key-value \r\n
+ // BOUNDARY \r\n
+ //
+ // Content-Disposition: form-data; name="dump"; filename="dump" \r\n
+ // Content-Type: application/octet-stream \r\n \r\n
+ // <dump contents>
+ // \r\n BOUNDARY -- \r\n
+
+#if defined(OS_CHROMEOS)
+ CrashReporterWriter writer(temp_file_fd);
+#else
+ MimeWriter writer(temp_file_fd, mime_boundary);
+#endif
+ {
+ const char* product_name = "";
+ const char* version = "";
+
+ GetCrashReporterClient()->GetProductNameAndVersion(&product_name, &version);
+
+ writer.AddBoundary();
+ writer.AddPairString("prod", product_name);
+ writer.AddBoundary();
+ writer.AddPairString("ver", version);
+ writer.AddBoundary();
+ if (info.pid > 0) {
+ char pid_value_buf[kUint64StringSize];
+ uint64_t pid_value_len = my_uint64_len(info.pid);
+ my_uint64tos(pid_value_buf, info.pid, pid_value_len);
+ static const char pid_key_name[] = "pid";
+ writer.AddPairData(pid_key_name, sizeof(pid_key_name) - 1,
+ pid_value_buf, pid_value_len);
+ writer.AddBoundary();
+ }
+#if defined(OS_ANDROID)
+ // Addtional MIME blocks are added for logging on Android devices.
+ static const char android_build_id[] = "android_build_id";
+ static const char android_build_fp[] = "android_build_fp";
+ static const char device[] = "device";
+ static const char model[] = "model";
+ static const char brand[] = "brand";
+ static const char exception_info[] = "exception_info";
+
+ base::android::BuildInfo* android_build_info =
+ base::android::BuildInfo::GetInstance();
+ writer.AddPairString(
+ android_build_id, android_build_info->android_build_id());
+ writer.AddBoundary();
+ writer.AddPairString(
+ android_build_fp, android_build_info->android_build_fp());
+ writer.AddBoundary();
+ writer.AddPairString(device, android_build_info->device());
+ writer.AddBoundary();
+ writer.AddPairString(model, android_build_info->model());
+ writer.AddBoundary();
+ writer.AddPairString(brand, android_build_info->brand());
+ writer.AddBoundary();
+ if (android_build_info->java_exception_info() != nullptr) {
+ writer.AddPairString(exception_info,
+ android_build_info->java_exception_info());
+ writer.AddBoundary();
+ }
+#endif
+ writer.Flush();
+ }
+
+ if (info.process_start_time > 0) {
+ struct kernel_timeval tv;
+ if (!sys_gettimeofday(&tv, nullptr)) {
+ uint64_t time = kernel_timeval_to_ms(&tv);
+ if (time > info.process_start_time) {
+ time -= info.process_start_time;
+ char time_str[kUint64StringSize];
+ const unsigned time_len = my_uint64_len(time);
+ my_uint64tos(time_str, time, time_len);
+
+ static const char process_time_msg[] = "ptime";
+ writer.AddPairData(process_time_msg, sizeof(process_time_msg) - 1,
+ time_str, time_len);
+ writer.AddBoundary();
+ writer.Flush();
+ }
+ }
+ }
+
+ if (info.process_type_length) {
+ writer.AddPairString("ptype", info.process_type);
+ writer.AddBoundary();
+ writer.Flush();
+ }
+
+ if (info.distro_length) {
+ static const char distro_msg[] = "lsb-release";
+ writer.AddPairString(distro_msg, info.distro);
+ writer.AddBoundary();
+ writer.Flush();
+ }
+
+ if (info.oom_size) {
+ char oom_size_str[kUint64StringSize];
+ const unsigned oom_size_len = my_uint64_len(info.oom_size);
+ my_uint64tos(oom_size_str, info.oom_size, oom_size_len);
+ static const char oom_size_msg[] = "oom-size";
+ writer.AddPairData(oom_size_msg, sizeof(oom_size_msg) - 1,
+ oom_size_str, oom_size_len);
+ writer.AddBoundary();
+ writer.Flush();
+ }
+
+ if (info.crash_keys) {
+ CrashKeyStorage::Iterator crash_key_iterator(*info.crash_keys);
+ const CrashKeyStorage::Entry* entry;
+ while ((entry = crash_key_iterator.Next())) {
+ writer.AddPairString(entry->key, entry->value);
+ writer.AddBoundary();
+ writer.Flush();
+ }
+ }
+
+ writer.AddFileContents(g_dump_msg, dump_data, dump_size);
+#if defined(ADDRESS_SANITIZER)
+ // Append a multipart boundary and the contents of the AddressSanitizer log.
+ writer.AddBoundary();
+ writer.AddFileContents(g_log_msg, log_data, log_size);
+#endif
+ writer.AddEnd();
+ writer.Flush();
+
+ IGNORE_RET(sys_close(temp_file_fd));
+
+#if defined(OS_ANDROID)
+ if (info.filename) {
+ size_t filename_length = my_strlen(info.filename);
+
+ // If this was a file, we need to copy it to the right place and use the
+ // right file name so it gets uploaded by the browser.
+ const char msg[] = "Output crash dump file:";
+ WriteLog(msg, sizeof(msg) - 1);
+ WriteLog(info.filename, filename_length);
+
+ char pid_buf[kUint64StringSize];
+ size_t pid_str_length = my_uint64_len(info.pid);
+ my_uint64tos(pid_buf, info.pid, pid_str_length);
+ pid_buf[pid_str_length] = 0; // my_uint64tos() doesn't null-terminate.
+
+ size_t done_filename_len = filename_length + pid_str_length + 1;
+ char* done_filename = reinterpret_cast<char*>(
+ allocator.Alloc(done_filename_len));
+ // Rename the file such that the pid is the suffix in order signal to other
+ // processes that the minidump is complete. The advantage of using the pid
+ // as the suffix is that it is trivial to associate the minidump with the
+ // crashed process.
+ my_strlcpy(done_filename, info.filename, done_filename_len);
+ my_strlcat(done_filename, pid_buf, done_filename_len);
+ // Rename the minidump file to signal that it is complete.
+ if (rename(info.filename, done_filename)) {
+ const char failed_msg[] = "Failed to rename:";
+ WriteLog(failed_msg, sizeof(failed_msg) - 1);
+ WriteLog(info.filename, filename_length);
+ const char to_msg[] = "to";
+ WriteLog(to_msg, sizeof(to_msg) - 1);
+ WriteLog(done_filename, done_filename_len - 1);
+ }
+ }
+#endif
+
+ if (!info.upload)
+ return;
+
+ const pid_t child = sys_fork();
+ if (!child) {
+ // Spawned helper process.
+ //
+ // This code is called both when a browser is crashing (in which case,
+ // nothing really matters any more) and when a renderer/plugin crashes, in
+ // which case we need to continue.
+ //
+ // Since we are a multithreaded app, if we were just to fork(), we might
+ // grab file descriptors which have just been created in another thread and
+ // hold them open for too long.
+ //
+ // Thus, we have to loop and try and close everything.
+ const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0);
+ if (fd < 0) {
+ for (unsigned i = 3; i < 8192; ++i)
+ IGNORE_RET(sys_close(i));
+ } else {
+ google_breakpad::DirectoryReader reader(fd);
+ const char* name;
+ while (reader.GetNextEntry(&name)) {
+ int i;
+ if (my_strtoui(&i, name) && i > 2 && i != fd)
+ IGNORE_RET(sys_close(i));
+ reader.PopEntry();
+ }
+
+ IGNORE_RET(sys_close(fd));
+ }
+
+ IGNORE_RET(sys_setsid());
+
+ // Leave one end of a pipe in the upload process and watch for it getting
+ // closed by the upload process exiting.
+ int fds[2];
+ if (sys_pipe(fds) >= 0) {
+ const pid_t upload_child = sys_fork();
+ if (!upload_child) {
+ // Upload process.
+ IGNORE_RET(sys_close(fds[0]));
+ IGNORE_RET(sys_dup2(fds[1], 3));
+ ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf,
+ &allocator);
+ }
+
+ // Helper process.
+ if (upload_child > 0) {
+ IGNORE_RET(sys_close(fds[1]));
+
+ const size_t kCrashIdLength = 16;
+ char id_buf[kCrashIdLength + 1];
+ size_t bytes_read =
+ WaitForCrashReportUploadProcess(fds[0], kCrashIdLength, id_buf);
+ HandleCrashReportId(id_buf, bytes_read, kCrashIdLength);
+
+ if (sys_waitpid(upload_child, nullptr, WNOHANG) == 0) {
+ // Upload process is still around, kill it.
+ sys_kill(upload_child, SIGKILL);
+ }
+ }
+ }
+
+ // Helper process.
+ IGNORE_RET(sys_unlink(info.filename));
+#if defined(ADDRESS_SANITIZER)
+ IGNORE_RET(sys_unlink(info.log_filename));
+#endif
+ IGNORE_RET(sys_unlink(temp_file));
+ sys__exit(0);
+ }
+
+ // Main browser process.
+ if (child <= 0)
+ return;
+ (void) HANDLE_EINTR(sys_waitpid(child, nullptr, 0));
+}
+
+void InitCrashReporter(const std::string& process_type) {
+#if defined(OS_ANDROID)
+ // This will guarantee that the BuildInfo has been initialized and subsequent
+ // calls will not require memory allocation.
+ base::android::BuildInfo::GetInstance();
+
+ // Handler registration is LIFO. Install the microdump handler first, such
+ // that if conventional minidump crash reporting is enabled below, it takes
+ // precedence (i.e. its handler is run first) over the microdump handler.
+ InitMicrodumpCrashHandlerIfNecessary(process_type);
+#endif
+ // Determine the process type and take appropriate action.
+ const base::CommandLine& parsed_command_line =
+ *base::CommandLine::ForCurrentProcess();
+ if (parsed_command_line.HasSwitch(switches::kDisableBreakpad))
+ return;
+
+ if (process_type.empty()) {
+ bool enable_breakpad = GetCrashReporterClient()->GetCollectStatsConsent() ||
+ GetCrashReporterClient()->IsRunningUnattended();
+ enable_breakpad &=
+ !parsed_command_line.HasSwitch(switches::kDisableBreakpad);
+ if (!enable_breakpad) {
+ enable_breakpad = parsed_command_line.HasSwitch(
+ switches::kEnableCrashReporterForTesting);
+ }
+ if (!enable_breakpad) {
+ VLOG(1) << "Breakpad disabled";
+ return;
+ }
+
+ InitCrashKeys();
+ EnableCrashDumping(GetCrashReporterClient()->IsRunningUnattended());
+ } else if (GetCrashReporterClient()->EnableBreakpadForProcess(process_type)) {
+#if defined(OS_ANDROID)
+ NOTREACHED() << "Breakpad initialized with InitCrashReporter() instead of "
+ "InitNonBrowserCrashReporter in " << process_type << " process.";
+ return;
+#else
+ // We might be chrooted in a zygote or renderer process so we cannot call
+ // GetCollectStatsConsent because that needs access the the user's home
+ // dir. Instead, we set a command line flag for these processes.
+ // Even though plugins are not chrooted, we share the same code path for
+ // simplicity.
+ if (!parsed_command_line.HasSwitch(switches::kEnableCrashReporter))
+ return;
+ InitCrashKeys();
+ SetClientIdFromCommandLine(parsed_command_line);
+ EnableNonBrowserCrashDumping();
+ VLOG(1) << "Non Browser crash dumping enabled for: " << process_type;
+#endif // #if defined(OS_ANDROID)
+ }
+
+ PostEnableBreakpadInitialization();
+}
+
+#if defined(OS_ANDROID)
+void InitNonBrowserCrashReporterForAndroid(const std::string& process_type) {
+ const base::CommandLine* command_line =
+ base::CommandLine::ForCurrentProcess();
+
+ // Handler registration is LIFO. Install the microdump handler first, such
+ // that if conventional minidump crash reporting is enabled below, it takes
+ // precedence (i.e. its handler is run first) over the microdump handler.
+ InitMicrodumpCrashHandlerIfNecessary(process_type);
+
+ if (command_line->HasSwitch(switches::kEnableCrashReporter)) {
+ // On Android we need to provide a FD to the file where the minidump is
+ // generated as the renderer and browser run with different UIDs
+ // (preventing the browser from inspecting the renderer process).
+ int minidump_fd = base::GlobalDescriptors::GetInstance()->MaybeGet(
+ GetCrashReporterClient()->GetAndroidMinidumpDescriptor());
+ if (minidump_fd < 0) {
+ NOTREACHED() << "Could not find minidump FD, crash reporting disabled.";
+ } else {
+ InitCrashKeys();
+ EnableNonBrowserCrashDumping(process_type, minidump_fd);
+ }
+ }
+}
+#endif // OS_ANDROID
+
+bool IsCrashReporterEnabled() {
+ return g_is_crash_reporter_enabled;
+}
+
+} // namespace breakpad
diff --git a/components/crash/content/app/breakpad_linux.h b/components/crash/content/app/breakpad_linux.h
new file mode 100644
index 0000000..4bfe0ce
--- /dev/null
+++ b/components/crash/content/app/breakpad_linux.h
@@ -0,0 +1,32 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Public interface for enabling Breakpad on Linux systems.
+
+#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
+#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
+
+#include <string>
+
+#include "build/build_config.h"
+
+namespace breakpad {
+
+// Turns on the crash reporter in any process.
+extern void InitCrashReporter(const std::string& process_type);
+
+// Enables the crash reporter in child processes.
+#if defined(OS_ANDROID)
+extern void InitNonBrowserCrashReporterForAndroid(
+ const std::string& process_type);
+#endif
+
+// Checks if crash reporting is enabled. Note that this is not the same as
+// being opted into metrics reporting (and crash reporting), which controls
+// whether InitCrashReporter() is called.
+bool IsCrashReporterEnabled();
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
diff --git a/components/crash/content/app/breakpad_linux_impl.h b/components/crash/content/app/breakpad_linux_impl.h
new file mode 100644
index 0000000..3c94d72
--- /dev/null
+++ b/components/crash/content/app/breakpad_linux_impl.h
@@ -0,0 +1,65 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Internal header file for the Linux breakpad implementation. This file is
+// shared between crash_handler_host_linux.cc and breakpad_linux.cc. It should
+// only be used in files compiled with linux_breakpad=1.
+
+#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_IMPL_H_
+#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_IMPL_H_
+
+#include <sys/types.h>
+
+#include "base/basictypes.h"
+#include "breakpad/src/common/simple_string_dictionary.h"
+#include "components/crash/content/app/breakpad_linux.h"
+
+namespace breakpad {
+
+typedef google_breakpad::NonAllocatingMap<256, 256, 64> CrashKeyStorage;
+
+#if defined(ADDRESS_SANITIZER)
+static const size_t kMaxAsanReportSize = 1 << 16;
+#endif
+// Define a preferred limit on minidump sizes, because Crash Server currently
+// throws away any larger than 1.2MB (1.2 * 1024 * 1024). A value of -1 means
+// no limit.
+static const off_t kMaxMinidumpFileSize = 1258291;
+
+// The size of the iovec used to transfer crash data from a child back to the
+// browser.
+#if !defined(ADDRESS_SANITIZER)
+const size_t kCrashIovSize = 6;
+#else
+// Additional field to pass the AddressSanitizer log to the crash handler.
+const size_t kCrashIovSize = 7;
+#endif
+
+// BreakpadInfo describes a crash report.
+// The minidump information can either be contained in a file descriptor (fd) or
+// in a file (whose path is in filename).
+struct BreakpadInfo {
+ int fd; // File descriptor to the Breakpad dump data.
+ const char* filename; // Path to the Breakpad dump data.
+#if defined(ADDRESS_SANITIZER)
+ const char* log_filename; // Path to the ASan log file.
+ const char* asan_report_str; // ASan report.
+ unsigned asan_report_length; // Length of |asan_report_length|.
+#endif
+ const char* process_type; // Process type, e.g. "renderer".
+ unsigned process_type_length; // Length of |process_type|.
+ const char* distro; // Linux distro string.
+ unsigned distro_length; // Length of |distro|.
+ bool upload; // Whether to upload or save crash dump.
+ uint64_t process_start_time; // Uptime of the crashing process.
+ size_t oom_size; // Amount of memory requested if OOM.
+ uint64_t pid; // PID where applicable.
+ CrashKeyStorage* crash_keys;
+};
+
+extern void HandleCrashDump(const BreakpadInfo& info);
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_IMPL_H_
diff --git a/components/crash/content/app/breakpad_mac.h b/components/crash/content/app/breakpad_mac.h
new file mode 100644
index 0000000..d392e13
--- /dev/null
+++ b/components/crash/content/app/breakpad_mac.h
@@ -0,0 +1,27 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_MAC_H_
+#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_MAC_H_
+
+#include <string>
+
+// This header defines the entry points for Breakpad integration.
+
+namespace breakpad {
+
+// Initializes Breakpad.
+void InitCrashReporter(const std::string& process_type);
+
+// Give Breakpad a chance to store information about the current process.
+// Extra information requires a parsed command line, so call this after
+// CommandLine::Init has been called.
+void InitCrashProcessInfo(const std::string& process_type_switch);
+
+// Is Breakpad enabled?
+bool IsCrashReporterEnabled();
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_MAC_H_
diff --git a/components/crash/content/app/breakpad_mac.mm b/components/crash/content/app/breakpad_mac.mm
new file mode 100644
index 0000000..84ef742
--- /dev/null
+++ b/components/crash/content/app/breakpad_mac.mm
@@ -0,0 +1,286 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "components/crash/content/app/breakpad_mac.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#import <Foundation/Foundation.h>
+
+#include "base/auto_reset.h"
+#include "base/base_switches.h"
+#import "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#import "base/logging.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#import "base/mac/scoped_nsautorelease_pool.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_restrictions.h"
+#import "breakpad/src/client/mac/Framework/Breakpad.h"
+#include "components/crash/content/app/crash_reporter_client.h"
+
+using crash_reporter::GetCrashReporterClient;
+
+namespace breakpad {
+
+namespace {
+
+BreakpadRef gBreakpadRef = NULL;
+
+void SetCrashKeyValue(NSString* key, NSString* value) {
+ // Comment repeated from header to prevent confusion:
+ // IMPORTANT: On OS X, the key/value pairs are sent to the crash server
+ // out of bounds and not recorded on disk in the minidump, this means
+ // that if you look at the minidump file locally you won't see them!
+ if (gBreakpadRef == NULL) {
+ return;
+ }
+
+ BreakpadAddUploadParameter(gBreakpadRef, key, value);
+}
+
+void ClearCrashKeyValue(NSString* key) {
+ if (gBreakpadRef == NULL) {
+ return;
+ }
+
+ BreakpadRemoveUploadParameter(gBreakpadRef, key);
+}
+
+void SetCrashKeyValueImpl(const base::StringPiece& key,
+ const base::StringPiece& value) {
+ @autoreleasepool {
+ SetCrashKeyValue(base::SysUTF8ToNSString(key.as_string()),
+ base::SysUTF8ToNSString(value.as_string()));
+ }
+}
+
+void ClearCrashKeyValueImpl(const base::StringPiece& key) {
+ @autoreleasepool {
+ ClearCrashKeyValue(base::SysUTF8ToNSString(key.as_string()));
+ }
+}
+
+bool FatalMessageHandler(int severity, const char* file, int line,
+ size_t message_start, const std::string& str) {
+ // Do not handle non-FATAL.
+ if (severity != logging::LOG_FATAL)
+ return false;
+
+ // In case of OOM condition, this code could be reentered when
+ // constructing and storing the key. Using a static is not
+ // thread-safe, but if multiple threads are in the process of a
+ // fatal crash at the same time, this should work.
+ static bool guarded = false;
+ if (guarded)
+ return false;
+
+ base::AutoReset<bool> guard(&guarded, true);
+
+ // Only log last path component. This matches logging.cc.
+ if (file) {
+ const char* slash = strrchr(file, '/');
+ if (slash)
+ file = slash + 1;
+ }
+
+ NSString* fatal_key = @"LOG_FATAL";
+ NSString* fatal_value =
+ [NSString stringWithFormat:@"%s:%d: %s",
+ file, line, str.c_str() + message_start];
+ SetCrashKeyValue(fatal_key, fatal_value);
+
+ // Rather than including the code to force the crash here, allow the
+ // caller to do it.
+ return false;
+}
+
+// BreakpadGenerateAndSendReport() does not report the current
+// thread. This class can be used to spin up a thread to run it.
+class DumpHelper : public base::PlatformThread::Delegate {
+ public:
+ static void DumpWithoutCrashing() {
+ DumpHelper dumper;
+ base::PlatformThreadHandle handle;
+ if (base::PlatformThread::Create(0, &dumper, &handle)) {
+ // The entire point of this is to block so that the correct
+ // stack is logged.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ base::PlatformThread::Join(handle);
+ }
+ }
+
+ private:
+ DumpHelper() {}
+
+ void ThreadMain() override {
+ base::PlatformThread::SetName("CrDumpHelper");
+ BreakpadGenerateAndSendReport(gBreakpadRef);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(DumpHelper);
+};
+
+void SIGABRTHandler(int signal) {
+ // The OSX abort() (link below) masks all signals for the process,
+ // and all except SIGABRT for the thread. SIGABRT will be masked
+ // when the SIGABRT is sent, which means at this point only SIGKILL
+ // and SIGSTOP can be delivered. Unmask others so that the code
+ // below crashes as desired.
+ //
+ // http://www.opensource.apple.com/source/Libc/Libc-825.26/stdlib/FreeBSD/abort.c
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, signal);
+ pthread_sigmask(SIG_SETMASK, &mask, NULL);
+
+ // Most interesting operations are not safe in a signal handler, just crash.
+ char* volatile death_ptr = NULL;
+ *death_ptr = '!';
+}
+
+} // namespace
+
+bool IsCrashReporterEnabled() {
+ return gBreakpadRef != NULL;
+}
+
+// Only called for a branded build of Chrome.app.
+void InitCrashReporter(const std::string& process_type) {
+ DCHECK(!gBreakpadRef);
+ base::mac::ScopedNSAutoreleasePool autorelease_pool;
+
+ // Check whether crash reporting should be enabled. If enterprise
+ // configuration management controls crash reporting, it takes precedence.
+ // Otherwise, check whether the user has consented to stats and crash
+ // reporting. The browser process can make this determination directly.
+ // Helper processes may not have access to the disk or to the same data as
+ // the browser process, so the browser passes the decision to them on the
+ // command line.
+ NSBundle* main_bundle = base::mac::FrameworkBundle();
+ bool is_browser = !base::mac::IsBackgroundOnlyProcess();
+ bool enable_breakpad = false;
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+ if (is_browser) {
+ // Since the configuration management infrastructure is possibly not
+ // initialized when this code runs, read the policy preference directly.
+ if (!GetCrashReporterClient()->ReportingIsEnforcedByPolicy(
+ &enable_breakpad)) {
+ // Controlled by the user. The crash reporter may be enabled by
+ // preference or through an environment variable, but the kDisableBreakpad
+ // switch overrides both.
+ enable_breakpad = GetCrashReporterClient()->GetCollectStatsConsent() ||
+ GetCrashReporterClient()->IsRunningUnattended();
+ enable_breakpad &= !command_line->HasSwitch(switches::kDisableBreakpad);
+ }
+ } else {
+ // This is a helper process, check the command line switch.
+ enable_breakpad = command_line->HasSwitch(switches::kEnableCrashReporter);
+ }
+
+ if (!enable_breakpad) {
+ VLOG_IF(1, is_browser) << "Breakpad disabled";
+ return;
+ }
+
+ // Tell Breakpad where crash_inspector and crash_report_sender are.
+ NSString* resource_path = [main_bundle resourcePath];
+ NSString *inspector_location =
+ [resource_path stringByAppendingPathComponent:@"crash_inspector"];
+ NSString *reporter_bundle_location =
+ [resource_path stringByAppendingPathComponent:@"crash_report_sender.app"];
+ NSString *reporter_location =
+ [[NSBundle bundleWithPath:reporter_bundle_location] executablePath];
+
+ if (!inspector_location || !reporter_location) {
+ VLOG_IF(1, is_browser && base::mac::AmIBundled()) << "Breakpad disabled";
+ return;
+ }
+
+ NSDictionary* info_dictionary = [main_bundle infoDictionary];
+ NSMutableDictionary *breakpad_config =
+ [[info_dictionary mutableCopy] autorelease];
+ [breakpad_config setObject:inspector_location
+ forKey:@BREAKPAD_INSPECTOR_LOCATION];
+ [breakpad_config setObject:reporter_location
+ forKey:@BREAKPAD_REPORTER_EXE_LOCATION];
+
+ // In the main application (the browser process), crashes can be passed to
+ // the system's Crash Reporter. This allows the system to notify the user
+ // when the application crashes, and provide the user with the option to
+ // restart it.
+ if (is_browser)
+ [breakpad_config setObject:@"NO" forKey:@BREAKPAD_SEND_AND_EXIT];
+
+ base::FilePath dir_crash_dumps;
+ GetCrashReporterClient()->GetCrashDumpLocation(&dir_crash_dumps);
+ [breakpad_config setObject:base::SysUTF8ToNSString(dir_crash_dumps.value())
+ forKey:@BREAKPAD_DUMP_DIRECTORY];
+
+ // Temporarily run Breakpad in-process on 10.10 and later because APIs that
+ // it depends on got broken (http://crbug.com/386208).
+ // This can catch crashes in the browser process only.
+ if (is_browser && base::mac::IsOSYosemiteOrLater()) {
+ [breakpad_config setObject:[NSNumber numberWithBool:YES]
+ forKey:@BREAKPAD_IN_PROCESS];
+ }
+
+ // Initialize Breakpad.
+ gBreakpadRef = BreakpadCreate(breakpad_config);
+ if (!gBreakpadRef) {
+ LOG_IF(ERROR, base::mac::AmIBundled()) << "Breakpad initialization failed";
+ return;
+ }
+
+ // Initialize the scoped crash key system.
+ base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueImpl,
+ &ClearCrashKeyValueImpl);
+ GetCrashReporterClient()->RegisterCrashKeys();
+
+ // Set Breakpad metadata values. These values are added to Info.plist during
+ // the branded Google Chrome.app build.
+ SetCrashKeyValue(@"ver", [info_dictionary objectForKey:@BREAKPAD_VERSION]);
+ SetCrashKeyValue(@"prod", [info_dictionary objectForKey:@BREAKPAD_PRODUCT]);
+ SetCrashKeyValue(@"plat", @"OS X");
+
+ logging::SetLogMessageHandler(&FatalMessageHandler);
+ base::debug::SetDumpWithoutCrashingFunction(&DumpHelper::DumpWithoutCrashing);
+
+ // abort() sends SIGABRT, which breakpad does not intercept.
+ // Register a signal handler to crash in a way breakpad will
+ // intercept.
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = SIGABRTHandler;
+ CHECK(0 == sigaction(SIGABRT, &sigact, NULL));
+}
+
+void InitCrashProcessInfo(const std::string& process_type_switch) {
+ if (gBreakpadRef == NULL) {
+ return;
+ }
+
+ // Determine the process type.
+ NSString* process_type = @"browser";
+ if (!process_type_switch.empty()) {
+ process_type = base::SysUTF8ToNSString(process_type_switch);
+ }
+
+ // Store process type in crash dump.
+ SetCrashKeyValue(@"ptype", process_type);
+
+ NSString* pid_value =
+ [NSString stringWithFormat:@"%d", static_cast<unsigned int>(getpid())];
+ SetCrashKeyValue(@"pid", pid_value);
+}
+
+} // namespace breakpad
diff --git a/components/crash/content/app/breakpad_mac_stubs.mm b/components/crash/content/app/breakpad_mac_stubs.mm
new file mode 100644
index 0000000..329794b
--- /dev/null
+++ b/components/crash/content/app/breakpad_mac_stubs.mm
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "components/crash/content/app/breakpad_mac.h"
+
+#import <Foundation/Foundation.h>
+
+// Stubbed out versions of breakpad integration functions so we can compile
+// without linking in Breakpad.
+
+namespace breakpad {
+
+bool IsCrashReporterEnabled() {
+ return false;
+}
+
+void InitCrashProcessInfo(const std::string& process_type_switch) {
+}
+
+void InitCrashReporter(const std::string& process_type) {
+}
+
+} // namespace breakpad
diff --git a/components/crash/content/app/breakpad_win.cc b/components/crash/content/app/breakpad_win.cc
new file mode 100644
index 0000000..5b53ff7
--- /dev/null
+++ b/components/crash/content/app/breakpad_win.cc
@@ -0,0 +1,745 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/app/breakpad_win.h"
+
+#include <windows.h>
+#include <shellapi.h>
+#include <tchar.h>
+#include <userenv.h>
+#include <winnt.h>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "base/base_switches.h"
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/environment.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "base/win/metro.h"
+#include "base/win/pe_image.h"
+#include "base/win/registry.h"
+#include "base/win/win_util.h"
+#include "breakpad/src/client/windows/handler/exception_handler.h"
+#include "components/crash/content/app/crash_keys_win.h"
+#include "components/crash/content/app/crash_reporter_client.h"
+#include "components/crash/content/app/hard_error_handler_win.h"
+#include "content/public/common/result_codes.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sidestep/preamble_patcher.h"
+
+// userenv.dll is required for GetProfileType().
+#pragma comment(lib, "userenv.lib")
+
+#pragma intrinsic(_AddressOfReturnAddress)
+#pragma intrinsic(_ReturnAddress)
+
+#ifdef _WIN64
+// See http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
+typedef struct _UNWIND_INFO {
+ unsigned char Version : 3;
+ unsigned char Flags : 5;
+ unsigned char SizeOfProlog;
+ unsigned char CountOfCodes;
+ unsigned char FrameRegister : 4;
+ unsigned char FrameOffset : 4;
+ ULONG ExceptionHandler;
+} UNWIND_INFO, *PUNWIND_INFO;
+#endif
+
+namespace breakpad {
+
+using crash_reporter::GetCrashReporterClient;
+
+namespace {
+
+// Minidump with stacks, PEB, TEB, and unloaded module list.
+const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithProcessThreadData | // Get PEB and TEB.
+ MiniDumpWithUnloadedModules); // Get unloaded modules when available.
+
+// Minidump with all of the above, plus memory referenced from stack.
+const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithProcessThreadData | // Get PEB and TEB.
+ MiniDumpWithUnloadedModules | // Get unloaded modules when available.
+ MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
+
+// Large dump with all process memory.
+const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithFullMemory | // Full memory from process.
+ MiniDumpWithProcessThreadData | // Get PEB and TEB.
+ MiniDumpWithHandleData | // Get all handle information.
+ MiniDumpWithUnloadedModules); // Get unloaded modules when available.
+
+const char kPipeNameVar[] = "CHROME_BREAKPAD_PIPE_NAME";
+
+const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
+const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
+
+// This is the well known SID for the system principal.
+const wchar_t kSystemPrincipalSid[] =L"S-1-5-18";
+
+google_breakpad::ExceptionHandler* g_breakpad = NULL;
+google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL;
+
+#if !defined(_WIN64)
+EXCEPTION_POINTERS g_surrogate_exception_pointers = {0};
+EXCEPTION_RECORD g_surrogate_exception_record = {0};
+CONTEXT g_surrogate_context = {0};
+#endif // !defined(_WIN64)
+
+typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle,
+ NTSTATUS ExitStatus);
+char* g_real_terminate_process_stub = NULL;
+
+} // namespace
+
+// Dumps the current process memory.
+extern "C" void __declspec(dllexport) __cdecl DumpProcess() {
+ if (g_breakpad) {
+ g_breakpad->WriteMinidump();
+ }
+}
+
+// Used for dumping a process state when there is no crash.
+extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() {
+ if (g_dumphandler_no_crash) {
+ g_dumphandler_no_crash->WriteMinidump();
+ }
+}
+
+namespace {
+
+// We need to prevent ICF from folding DumpForHangDebuggingThread() and
+// DumpProcessWithoutCrashThread() together, since that makes them
+// indistinguishable in crash dumps. We do this by making the function
+// bodies unique, and prevent optimization from shuffling things around.
+MSVC_DISABLE_OPTIMIZE()
+MSVC_PUSH_DISABLE_WARNING(4748)
+
+DWORD WINAPI DumpProcessWithoutCrashThread(void*) {
+ DumpProcessWithoutCrash();
+ return 0;
+}
+
+// The following two functions do exactly the same thing as the two above. But
+// we want the signatures to be different so that we can easily track them in
+// crash reports.
+// TODO(yzshen): Remove when enough information is collected and the hang rate
+// of pepper/renderer processes is reduced.
+DWORD WINAPI DumpForHangDebuggingThread(void*) {
+ DumpProcessWithoutCrash();
+ VLOG(1) << "dumped for hang debugging";
+ return 0;
+}
+
+MSVC_POP_WARNING()
+MSVC_ENABLE_OPTIMIZE()
+
+} // namespace
+
+// Injects a thread into a remote process to dump state when there is no crash.
+extern "C" HANDLE __declspec(dllexport) __cdecl
+InjectDumpProcessWithoutCrash(HANDLE process) {
+ return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread,
+ 0, 0, NULL);
+}
+
+extern "C" HANDLE __declspec(dllexport) __cdecl
+InjectDumpForHangDebugging(HANDLE process) {
+ return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread,
+ 0, 0, NULL);
+}
+
+// Returns a string containing a list of all modifiers for the loaded profile.
+std::wstring GetProfileType() {
+ std::wstring profile_type;
+ DWORD profile_bits = 0;
+ if (::GetProfileType(&profile_bits)) {
+ static const struct {
+ DWORD bit;
+ const wchar_t* name;
+ } kBitNames[] = {
+ { PT_MANDATORY, L"mandatory" },
+ { PT_ROAMING, L"roaming" },
+ { PT_TEMPORARY, L"temporary" },
+ };
+ for (size_t i = 0; i < arraysize(kBitNames); ++i) {
+ const DWORD this_bit = kBitNames[i].bit;
+ if ((profile_bits & this_bit) != 0) {
+ profile_type.append(kBitNames[i].name);
+ profile_bits &= ~this_bit;
+ if (profile_bits != 0)
+ profile_type.append(L", ");
+ }
+ }
+ } else {
+ DWORD last_error = ::GetLastError();
+ base::SStringPrintf(&profile_type, L"error %u", last_error);
+ }
+ return profile_type;
+}
+
+namespace {
+
+// This callback is used when we want to get a dump without crashing the
+// process.
+bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*,
+ EXCEPTION_POINTERS* ex_info,
+ MDRawAssertionInfo*, bool succeeded) {
+ GetCrashReporterClient()->RecordCrashDumpAttemptResult(
+ false /* is_real_crash */, succeeded);
+ return true;
+}
+
+// This callback is executed when the browser process has crashed, after
+// the crash dump has been created. We need to minimize the amount of work
+// done here since we have potentially corrupted process. Our job is to
+// spawn another instance of chrome which will show a 'chrome has crashed'
+// dialog. This code needs to live in the exe and thus has no access to
+// facilities such as the i18n helpers.
+bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*,
+ EXCEPTION_POINTERS* ex_info,
+ MDRawAssertionInfo*, bool succeeded) {
+ GetCrashReporterClient()->RecordCrashDumpAttemptResult(
+ true /* is_real_crash */, succeeded);
+ // Check if the exception is one of the kind which would not be solved
+ // by simply restarting chrome. In this case we show a message box with
+ // and exit silently. Remember that chrome is in a crashed state so we
+ // can't show our own UI from this process.
+ if (HardErrorHandler(ex_info))
+ return true;
+
+ if (!GetCrashReporterClient()->AboutToRestart())
+ return true;
+
+ // Now we just start chrome browser with the same command line.
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi;
+ if (::CreateProcessW(NULL, ::GetCommandLineW(), NULL, NULL, FALSE,
+ CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) {
+ ::CloseHandle(pi.hProcess);
+ ::CloseHandle(pi.hThread);
+ }
+ // After this return we will be terminated. The actual return value is
+ // not used at all.
+ return true;
+}
+
+// flag to indicate that we are already handling an exception.
+volatile LONG handling_exception = 0;
+
+// This callback is used when there is no crash. Note: Unlike the
+// |FilterCallback| below this does not do dupe detection. It is upto the caller
+// to implement it.
+bool FilterCallbackWhenNoCrash(
+ void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) {
+ GetCrashReporterClient()->RecordCrashDumpAttempt(false);
+ return true;
+}
+
+// This callback is executed when the Chrome process has crashed and *before*
+// the crash dump is created. To prevent duplicate crash reports we
+// make every thread calling this method, except the very first one,
+// go to sleep.
+bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) {
+ // Capture every thread except the first one in the sleep. We don't
+ // want multiple threads to concurrently report exceptions.
+ if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) {
+ ::Sleep(INFINITE);
+ }
+ GetCrashReporterClient()->RecordCrashDumpAttempt(true);
+ return true;
+}
+
+// Previous unhandled filter. Will be called if not null when we
+// intercept a crash.
+LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL;
+
+// Exception filter used when breakpad is not enabled. We just display
+// the "Do you want to restart" message and then we call the previous filter.
+long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) {
+ DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
+
+ if (previous_filter)
+ return previous_filter(info);
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+// Exception filter for the service process used when breakpad is not enabled.
+// We just display the "Do you want to restart" message and then die
+// (without calling the previous filter).
+long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) {
+ DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+#if !defined(COMPONENT_BUILD)
+// Installed via base::debug::SetCrashKeyReportingFunctions.
+void SetCrashKeyValueForBaseDebug(const base::StringPiece& key,
+ const base::StringPiece& value) {
+ DCHECK(CrashKeysWin::keeper());
+ CrashKeysWin::keeper()->SetCrashKeyValue(base::UTF8ToUTF16(key),
+ base::UTF8ToUTF16(value));
+}
+
+// Installed via base::debug::SetCrashKeyReportingFunctions.
+void ClearCrashKeyForBaseDebug(const base::StringPiece& key) {
+ DCHECK(CrashKeysWin::keeper());
+ CrashKeysWin::keeper()->ClearCrashKeyValue(base::UTF8ToUTF16(key));
+}
+#endif // !defined(COMPONENT_BUILD)
+
+} // namespace
+
+// NOTE: This function is used by SyzyASAN to annotate crash reports. If you
+// change the name or signature of this function you will break SyzyASAN
+// instrumented releases of Chrome. Please contact syzygy-team@chromium.org
+// before doing so!
+extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl(
+ const wchar_t* key, const wchar_t* value) {
+ CrashKeysWin* keeper = CrashKeysWin::keeper();
+ if (!keeper)
+ return;
+
+ // TODO(siggi): This doesn't look quite right - there's NULL deref potential
+ // here, and an implicit std::wstring conversion. Fixme.
+ keeper->SetCrashKeyValue(key, value);
+}
+
+extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl(
+ const wchar_t* key) {
+ CrashKeysWin* keeper = CrashKeysWin::keeper();
+ if (!keeper)
+ return;
+
+ // TODO(siggi): This doesn't look quite right - there's NULL deref potential
+ // here, and an implicit std::wstring conversion. Fixme.
+ keeper->ClearCrashKeyValue(key);
+}
+
+static bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption,
+ UINT flags, bool* exit_now) {
+ // We wrap the call to MessageBoxW with a SEH handler because it some
+ // machines with CursorXP, PeaDict or with FontExplorer installed it crashes
+ // uncontrollably here. Being this a best effort deal we better go away.
+ __try {
+ *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags));
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ // Its not safe to continue executing, exit silently here.
+ ::TerminateProcess(::GetCurrentProcess(),
+ GetCrashReporterClient()->GetResultCodeRespawnFailed());
+ }
+
+ return true;
+}
+
+// This function is executed by the child process that DumpDoneCallback()
+// spawned and basically just shows the 'chrome has crashed' dialog if
+// the CHROME_CRASHED environment variable is present.
+bool ShowRestartDialogIfCrashed(bool* exit_now) {
+ // If we are being launched in metro mode don't try to show the dialog.
+ if (base::win::IsMetroProcess())
+ return false;
+
+ base::string16 message;
+ base::string16 title;
+ bool is_rtl_locale;
+ if (!GetCrashReporterClient()->ShouldShowRestartDialog(
+ &title, &message, &is_rtl_locale)) {
+ return false;
+ }
+
+ // If the UI layout is right-to-left, we need to pass the appropriate MB_XXX
+ // flags so that an RTL message box is displayed.
+ UINT flags = MB_OKCANCEL | MB_ICONWARNING;
+ if (is_rtl_locale)
+ flags |= MB_RIGHT | MB_RTLREADING;
+
+ return WrapMessageBoxWithSEH(message.c_str(), title.c_str(), flags, exit_now);
+}
+
+extern "C" void __declspec(dllexport) TerminateProcessWithoutDump() {
+ // Patched stub exists based on conditions (See InitCrashReporter).
+ // As a side note this function also gets called from
+ // WindowProcExceptionFilter.
+ if (g_real_terminate_process_stub == NULL) {
+ ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
+ } else {
+ NtTerminateProcessPtr real_terminate_proc =
+ reinterpret_cast<NtTerminateProcessPtr>(
+ static_cast<char*>(g_real_terminate_process_stub));
+ real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
+ }
+}
+
+// Crashes the process after generating a dump for the provided exception. Note
+// that the crash reporter should be initialized before calling this function
+// for it to do anything.
+// NOTE: This function is used by SyzyASAN to invoke a crash. If you change the
+// the name or signature of this function you will break SyzyASAN instrumented
+// releases of Chrome. Please contact syzygy-team@chromium.org before doing so!
+extern "C" int __declspec(dllexport) CrashForException(
+ EXCEPTION_POINTERS* info) {
+ if (g_breakpad) {
+ g_breakpad->WriteMinidumpForException(info);
+ TerminateProcessWithoutDump();
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+#ifndef _WIN64
+static NTSTATUS WINAPI HookNtTerminateProcess(HANDLE ProcessHandle,
+ NTSTATUS ExitStatus) {
+ if (g_breakpad &&
+ (ProcessHandle == ::GetCurrentProcess() || ProcessHandle == NULL)) {
+ NT_TIB* tib = reinterpret_cast<NT_TIB*>(NtCurrentTeb());
+ void* address_on_stack = _AddressOfReturnAddress();
+ if (address_on_stack < tib->StackLimit ||
+ address_on_stack > tib->StackBase) {
+ g_surrogate_exception_record.ExceptionAddress = _ReturnAddress();
+ g_surrogate_exception_record.ExceptionCode = DBG_TERMINATE_PROCESS;
+ g_surrogate_exception_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ CrashForException(&g_surrogate_exception_pointers);
+ }
+ }
+
+ NtTerminateProcessPtr real_proc =
+ reinterpret_cast<NtTerminateProcessPtr>(
+ static_cast<char*>(g_real_terminate_process_stub));
+ return real_proc(ProcessHandle, ExitStatus);
+}
+
+static void InitTerminateProcessHooks() {
+ NtTerminateProcessPtr terminate_process_func_address =
+ reinterpret_cast<NtTerminateProcessPtr>(::GetProcAddress(
+ ::GetModuleHandle(L"ntdll.dll"), "NtTerminateProcess"));
+ if (terminate_process_func_address == NULL)
+ return;
+
+ DWORD old_protect = 0;
+ if (!::VirtualProtect(terminate_process_func_address, 5,
+ PAGE_EXECUTE_READWRITE, &old_protect))
+ return;
+
+ g_real_terminate_process_stub = reinterpret_cast<char*>(VirtualAllocEx(
+ ::GetCurrentProcess(), NULL, sidestep::kMaxPreambleStubSize,
+ MEM_COMMIT, PAGE_EXECUTE_READWRITE));
+ if (g_real_terminate_process_stub == NULL)
+ return;
+
+ g_surrogate_exception_pointers.ContextRecord = &g_surrogate_context;
+ g_surrogate_exception_pointers.ExceptionRecord =
+ &g_surrogate_exception_record;
+
+ sidestep::SideStepError patch_result =
+ sidestep::PreamblePatcher::Patch(
+ terminate_process_func_address, HookNtTerminateProcess,
+ g_real_terminate_process_stub, sidestep::kMaxPreambleStubSize);
+ if (patch_result != sidestep::SIDESTEP_SUCCESS) {
+ CHECK(::VirtualFreeEx(::GetCurrentProcess(), g_real_terminate_process_stub,
+ 0, MEM_RELEASE));
+ CHECK(::VirtualProtect(terminate_process_func_address, 5, old_protect,
+ &old_protect));
+ return;
+ }
+
+ DWORD dummy = 0;
+ CHECK(::VirtualProtect(terminate_process_func_address,
+ 5,
+ old_protect,
+ &dummy));
+ CHECK(::VirtualProtect(g_real_terminate_process_stub,
+ sidestep::kMaxPreambleStubSize,
+ old_protect,
+ &old_protect));
+}
+#endif
+
+static void InitPipeNameEnvVar(bool is_per_user_install) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ if (env->HasVar(kPipeNameVar)) {
+ // The Breakpad pipe name is already configured: nothing to do.
+ return;
+ }
+
+ // Check whether configuration management controls crash reporting.
+ bool crash_reporting_enabled = true;
+ bool controlled_by_policy =
+ GetCrashReporterClient()->ReportingIsEnforcedByPolicy(
+ &crash_reporting_enabled);
+
+ const base::CommandLine& command = *base::CommandLine::ForCurrentProcess();
+ bool use_crash_service = !controlled_by_policy &&
+ (command.HasSwitch(switches::kNoErrorDialogs) ||
+ GetCrashReporterClient()->IsRunningUnattended());
+
+ std::wstring pipe_name;
+ if (use_crash_service) {
+ // Crash reporting is done by crash_service.exe.
+ pipe_name = kChromePipeName;
+ } else {
+ // We want to use the Google Update crash reporting. We need to check if the
+ // user allows it first (in case the administrator didn't already decide
+ // via policy).
+ if (!controlled_by_policy)
+ crash_reporting_enabled =
+ GetCrashReporterClient()->GetCollectStatsConsent();
+
+ if (!crash_reporting_enabled) {
+ // Crash reporting is disabled, don't set the environment variable.
+ return;
+ }
+
+ // Build the pipe name. It can be either:
+ // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
+ // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
+ std::wstring user_sid;
+ if (is_per_user_install) {
+ if (!base::win::GetUserSidString(&user_sid)) {
+ return;
+ }
+ } else {
+ user_sid = kSystemPrincipalSid;
+ }
+
+ pipe_name = kGoogleUpdatePipeName;
+ pipe_name += user_sid;
+ }
+ env->SetVar(kPipeNameVar, base::UTF16ToASCII(pipe_name));
+}
+
+void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) {
+ previous_filter = SetUnhandledExceptionFilter(filter);
+}
+
+void InitCrashReporter(const std::string& process_type_switch) {
+ const base::CommandLine& command = *base::CommandLine::ForCurrentProcess();
+ if (command.HasSwitch(switches::kDisableBreakpad))
+ return;
+
+ // Disable the message box for assertions.
+ _CrtSetReportMode(_CRT_ASSERT, 0);
+
+ base::string16 process_type = base::ASCIIToUTF16(process_type_switch);
+ if (process_type.empty())
+ process_type = L"browser";
+
+ wchar_t exe_path[MAX_PATH];
+ exe_path[0] = 0;
+ GetModuleFileNameW(NULL, exe_path, MAX_PATH);
+
+ bool is_per_user_install =
+ GetCrashReporterClient()->GetIsPerUserInstall(base::FilePath(exe_path));
+
+ // This is intentionally leaked.
+ CrashKeysWin* keeper = new CrashKeysWin();
+
+ google_breakpad::CustomClientInfo* custom_info =
+ keeper->GetCustomInfo(exe_path, process_type, GetProfileType(),
+ base::CommandLine::ForCurrentProcess(),
+ GetCrashReporterClient());
+
+#if !defined(COMPONENT_BUILD)
+ // chrome/common/child_process_logging_win.cc registers crash keys for
+ // chrome.dll. In a component build, that is sufficient as chrome.dll and
+ // chrome.exe share a copy of base (in base.dll).
+ // In a static build, the EXE must separately initialize the crash keys
+ // configuration as it has its own statically linked copy of base.
+ base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueForBaseDebug,
+ &ClearCrashKeyForBaseDebug);
+ GetCrashReporterClient()->RegisterCrashKeys();
+#endif
+
+ google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL;
+ LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL;
+ // We install the post-dump callback only for the browser and service
+ // processes. It spawns a new browser/service process.
+ if (process_type == L"browser") {
+ callback = &DumpDoneCallback;
+ default_filter = &ChromeExceptionFilter;
+ } else if (process_type == L"service") {
+ callback = &DumpDoneCallback;
+ default_filter = &ServiceExceptionFilter;
+ }
+
+ if (process_type == L"browser") {
+ InitPipeNameEnvVar(is_per_user_install);
+ GetCrashReporterClient()->InitBrowserCrashDumpsRegKey();
+ }
+
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ std::string pipe_name_ascii;
+ if (!env->GetVar(kPipeNameVar, &pipe_name_ascii)) {
+ // Breakpad is not enabled. Configuration is managed or the user
+ // did not allow Google Update to send crashes. We need to use
+ // our default crash handler instead, but only for the
+ // browser/service processes.
+ if (default_filter)
+ InitDefaultCrashCallback(default_filter);
+ return;
+ }
+ base::string16 pipe_name = base::ASCIIToUTF16(pipe_name_ascii);
+
+#ifdef _WIN64
+ // The protocol for connecting to the out-of-process Breakpad crash
+ // reporter is different for x86-32 and x86-64: the message sizes
+ // are different because the message struct contains a pointer. As
+ // a result, there are two different named pipes to connect to. The
+ // 64-bit one is distinguished with an "-x64" suffix.
+ pipe_name += L"-x64";
+#endif
+
+ // Get the alternate dump directory. We use the temp path.
+ wchar_t temp_dir[MAX_PATH] = {0};
+ ::GetTempPathW(MAX_PATH, temp_dir);
+
+ MINIDUMP_TYPE dump_type = kSmallDumpType;
+ // Capture full memory if explicitly instructed to.
+ if (command.HasSwitch(switches::kFullMemoryCrashReport))
+ dump_type = kFullDumpType;
+ else if (GetCrashReporterClient()->GetShouldDumpLargerDumps(
+ is_per_user_install))
+ dump_type = kLargerDumpType;
+
+ g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback,
+ callback, NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL,
+ dump_type, pipe_name.c_str(), custom_info);
+
+ // Now initialize the non crash dump handler.
+ g_dumphandler_no_crash = new google_breakpad::ExceptionHandler(temp_dir,
+ &FilterCallbackWhenNoCrash,
+ &DumpDoneCallbackWhenNoCrash,
+ NULL,
+ // Set the handler to none so this handler would not be added to
+ // |handler_stack_| in |ExceptionHandler| which is a list of exception
+ // handlers.
+ google_breakpad::ExceptionHandler::HANDLER_NONE,
+ dump_type, pipe_name.c_str(), custom_info);
+
+ // Set the DumpWithoutCrashingFunction for this instance of base.lib. Other
+ // executable images linked with base should set this again for
+ // DumpWithoutCrashing to function correctly.
+ // See chrome_main.cc for example.
+ base::debug::SetDumpWithoutCrashingFunction(&DumpProcessWithoutCrash);
+
+ if (g_breakpad->IsOutOfProcess()) {
+ // Tells breakpad to handle breakpoint and single step exceptions.
+ // This might break JIT debuggers, but at least it will always
+ // generate a crashdump for these exceptions.
+ g_breakpad->set_handle_debug_exceptions(true);
+
+#ifndef _WIN64
+ if (process_type != L"browser" &&
+ !GetCrashReporterClient()->IsRunningUnattended()) {
+ // Initialize the hook TerminateProcess to catch unexpected exits.
+ InitTerminateProcessHooks();
+ }
+#endif
+ }
+}
+
+void ConsumeInvalidHandleExceptions() {
+ if (g_breakpad) {
+ g_breakpad->set_consume_invalid_handle_exceptions(true);
+ }
+ if (g_dumphandler_no_crash) {
+ g_dumphandler_no_crash->set_consume_invalid_handle_exceptions(true);
+ }
+}
+
+// If the user has disabled crash reporting uploads and restarted Chrome, the
+// restarted instance will still contain the pipe environment variable, which
+// will allow the restarted process to still upload crash reports. This function
+// clears the environment variable, so that the restarted Chrome, which inherits
+// its environment from the current Chrome, will no longer contain the variable.
+extern "C" void __declspec(dllexport) __cdecl
+ ClearBreakpadPipeEnvironmentVariable() {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ env->UnSetVar(kPipeNameVar);
+}
+
+#ifdef _WIN64
+int CrashForExceptionInNonABICompliantCodeRange(
+ PEXCEPTION_RECORD ExceptionRecord,
+ ULONG64 EstablisherFrame,
+ PCONTEXT ContextRecord,
+ PDISPATCHER_CONTEXT DispatcherContext) {
+ EXCEPTION_POINTERS info = { ExceptionRecord, ContextRecord };
+ return CrashForException(&info);
+}
+
+struct ExceptionHandlerRecord {
+ RUNTIME_FUNCTION runtime_function;
+ UNWIND_INFO unwind_info;
+ unsigned char thunk[12];
+};
+
+extern "C" void __declspec(dllexport) __cdecl
+RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) {
+ ExceptionHandlerRecord* record =
+ reinterpret_cast<ExceptionHandlerRecord*>(start);
+
+ // We assume that the first page of the code range is executable and
+ // committed and reserved for breakpad. What could possibly go wrong?
+
+ // All addresses are 32bit relative offsets to start.
+ record->runtime_function.BeginAddress = 0;
+ record->runtime_function.EndAddress =
+ base::checked_cast<DWORD>(size_in_bytes);
+ record->runtime_function.UnwindData =
+ offsetof(ExceptionHandlerRecord, unwind_info);
+
+ // Create unwind info that only specifies an exception handler.
+ record->unwind_info.Version = 1;
+ record->unwind_info.Flags = UNW_FLAG_EHANDLER;
+ record->unwind_info.SizeOfProlog = 0;
+ record->unwind_info.CountOfCodes = 0;
+ record->unwind_info.FrameRegister = 0;
+ record->unwind_info.FrameOffset = 0;
+ record->unwind_info.ExceptionHandler =
+ offsetof(ExceptionHandlerRecord, thunk);
+
+ // Hardcoded thunk.
+ // mov imm64, rax
+ record->thunk[0] = 0x48;
+ record->thunk[1] = 0xb8;
+ void* handler = &CrashForExceptionInNonABICompliantCodeRange;
+ memcpy(&record->thunk[2], &handler, 8);
+
+ // jmp rax
+ record->thunk[10] = 0xff;
+ record->thunk[11] = 0xe0;
+
+ // Protect reserved page against modifications.
+ DWORD old_protect;
+ CHECK(VirtualProtect(
+ start, sizeof(ExceptionHandlerRecord), PAGE_EXECUTE_READ, &old_protect));
+ CHECK(RtlAddFunctionTable(
+ &record->runtime_function, 1, reinterpret_cast<DWORD64>(start)));
+}
+
+extern "C" void __declspec(dllexport) __cdecl
+UnregisterNonABICompliantCodeRange(void* start) {
+ ExceptionHandlerRecord* record =
+ reinterpret_cast<ExceptionHandlerRecord*>(start);
+
+ CHECK(RtlDeleteFunctionTable(&record->runtime_function));
+}
+#endif
+
+} // namespace breakpad
diff --git a/components/crash/content/app/breakpad_win.h b/components/crash/content/app/breakpad_win.h
new file mode 100644
index 0000000..c4e9349
--- /dev/null
+++ b/components/crash/content/app/breakpad_win.h
@@ -0,0 +1,28 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_WIN_H_
+#define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_WIN_H_
+
+#include <windows.h>
+#include <string>
+#include <vector>
+
+namespace breakpad {
+
+void InitCrashReporter(const std::string& process_type_switch);
+
+// If chrome has been restarted because it crashed, this function will display
+// a dialog asking for permission to continue execution or to exit now.
+bool ShowRestartDialogIfCrashed(bool* exit_now);
+
+// Tells Breakpad that our process is shutting down and to consume
+// EXCEPTION_INVALID_HANDLE exceptions which occur if bad handle detection is
+// enabled and the sandbox handle closer has previously closed handles owned by
+// Windows DLLs.
+void ConsumeInvalidHandleExceptions();
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_WIN_H_
diff --git a/components/crash/content/app/crash_keys_win.cc b/components/crash/content/app/crash_keys_win.cc
new file mode 100644
index 0000000..2c784fa
--- /dev/null
+++ b/components/crash/content/app/crash_keys_win.cc
@@ -0,0 +1,192 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/app/crash_keys_win.h"
+
+#include <algorithm>
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "components/crash/content/app/crash_reporter_client.h"
+
+namespace breakpad {
+
+using crash_reporter::CrashReporterClient;
+
+namespace {
+
+const size_t kMaxPluginPathLength = 256;
+const size_t kMaxDynamicEntries = 256;
+
+} // namespace
+
+CrashKeysWin* CrashKeysWin::keeper_;
+
+CrashKeysWin::CrashKeysWin() : dynamic_keys_offset_(0) {
+ DCHECK_EQ(static_cast<CrashKeysWin*>(NULL), keeper_);
+ keeper_ = this;
+}
+
+CrashKeysWin::~CrashKeysWin() {
+ DCHECK_EQ(this, keeper_);
+ keeper_ = NULL;
+}
+
+// Appends the plugin path to |g_custom_entries|.
+void CrashKeysWin::SetPluginPath(const std::wstring& path) {
+ if (path.size() > kMaxPluginPathLength) {
+ // If the path is too long, truncate from the start rather than the end,
+ // since we want to be able to recover the DLL name.
+ SetPluginPath(path.substr(path.size() - kMaxPluginPathLength));
+ return;
+ }
+
+ // The chunk size without terminator.
+ const size_t kChunkSize = static_cast<size_t>(
+ google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
+
+ int chunk_index = 0;
+ size_t chunk_start = 0; // Current position inside |path|
+
+ for (chunk_start = 0; chunk_start < path.size(); chunk_index++) {
+ size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start);
+
+ custom_entries_.push_back(google_breakpad::CustomInfoEntry(
+ base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(),
+ path.substr(chunk_start, chunk_length).c_str()));
+
+ chunk_start += chunk_length;
+ }
+}
+
+// Appends the breakpad dump path to |g_custom_entries|.
+void CrashKeysWin::SetBreakpadDumpPath(CrashReporterClient* crash_client) {
+ base::FilePath crash_dumps_dir_path;
+ if (crash_client->GetAlternativeCrashDumpLocation(&crash_dumps_dir_path)) {
+ custom_entries_.push_back(google_breakpad::CustomInfoEntry(
+ L"breakpad-dump-location", crash_dumps_dir_path.value().c_str()));
+ }
+}
+
+// Returns the custom info structure based on the dll in parameter and the
+// process type.
+google_breakpad::CustomClientInfo*
+CrashKeysWin::GetCustomInfo(const std::wstring& exe_path,
+ const std::wstring& type,
+ const std::wstring& profile_type,
+ base::CommandLine* cmd_line,
+ CrashReporterClient* crash_client) {
+ base::string16 version, product;
+ base::string16 special_build;
+ base::string16 channel_name;
+
+ crash_client->GetProductNameAndVersion(
+ base::FilePath(exe_path),
+ &product,
+ &version,
+ &special_build,
+ &channel_name);
+
+ // We only expect this method to be called once per process.
+ // Common enties
+ custom_entries_.push_back(
+ google_breakpad::CustomInfoEntry(L"ver", version.c_str()));
+ custom_entries_.push_back(
+ google_breakpad::CustomInfoEntry(L"prod", product.c_str()));
+ custom_entries_.push_back(
+ google_breakpad::CustomInfoEntry(L"plat", L"Win32"));
+ custom_entries_.push_back(
+ google_breakpad::CustomInfoEntry(L"ptype", type.c_str()));
+ custom_entries_.push_back(
+ google_breakpad::CustomInfoEntry(
+ L"pid", base::IntToString16(::GetCurrentProcessId()).c_str()));
+ custom_entries_.push_back(
+ google_breakpad::CustomInfoEntry(L"channel", channel_name.c_str()));
+ custom_entries_.push_back(
+ google_breakpad::CustomInfoEntry(L"profile-type", profile_type.c_str()));
+
+ if (!special_build.empty()) {
+ custom_entries_.push_back(
+ google_breakpad::CustomInfoEntry(L"special", special_build.c_str()));
+ }
+
+ if (type == L"plugin" || type == L"ppapi") {
+ std::wstring plugin_path = cmd_line->GetSwitchValueNative("plugin-path");
+ if (!plugin_path.empty())
+ SetPluginPath(plugin_path);
+ }
+
+ // Check whether configuration management controls crash reporting.
+ bool crash_reporting_enabled = true;
+ bool controlled_by_policy = crash_client->ReportingIsEnforcedByPolicy(
+ &crash_reporting_enabled);
+ bool use_crash_service = !controlled_by_policy &&
+ (cmd_line->HasSwitch(switches::kNoErrorDialogs) ||
+ crash_client->IsRunningUnattended());
+ if (use_crash_service)
+ SetBreakpadDumpPath(crash_client);
+
+ // Create space for dynamic ad-hoc keys. The names and values are set using
+ // the API defined in base/debug/crash_logging.h.
+ dynamic_keys_offset_ = custom_entries_.size();
+ for (size_t i = 0; i < kMaxDynamicEntries; ++i) {
+ // The names will be mutated as they are set. Un-numbered since these are
+ // merely placeholders. The name cannot be empty because Breakpad's
+ // HTTPUpload will interpret that as an invalid parameter.
+ custom_entries_.push_back(
+ google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L""));
+ }
+
+ static google_breakpad::CustomClientInfo custom_client_info;
+ custom_client_info.entries = &custom_entries_.front();
+ custom_client_info.count = custom_entries_.size();
+
+ return &custom_client_info;
+}
+
+void CrashKeysWin::SetCrashKeyValue(
+ const std::wstring& key, const std::wstring& value) {
+ // CustomInfoEntry limits the length of key and value. If they exceed
+ // their maximum length the underlying string handling functions raise
+ // an exception and prematurely trigger a crash. Truncate here.
+ std::wstring safe_key(std::wstring(key).substr(
+ 0, google_breakpad::CustomInfoEntry::kNameMaxLength - 1));
+ std::wstring safe_value(std::wstring(value).substr(
+ 0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1));
+
+ // If we already have a value for this key, update it; otherwise, insert
+ // the new value if we have not exhausted the pre-allocated slots for dynamic
+ // entries.
+ base::AutoLock lock(lock_);
+
+ DynamicEntriesMap::iterator it = dynamic_entries_.find(safe_key);
+ google_breakpad::CustomInfoEntry* entry = NULL;
+ if (it == dynamic_entries_.end()) {
+ if (dynamic_entries_.size() >= kMaxDynamicEntries)
+ return;
+ entry = &custom_entries_[dynamic_keys_offset_++];
+ dynamic_entries_.insert(std::make_pair(safe_key, entry));
+ } else {
+ entry = it->second;
+ }
+
+ entry->set(safe_key.data(), safe_value.data());
+}
+
+void CrashKeysWin::ClearCrashKeyValue(const std::wstring& key) {
+ base::AutoLock lock(lock_);
+
+ std::wstring key_string(key);
+ DynamicEntriesMap::iterator it = dynamic_entries_.find(key_string);
+ if (it == dynamic_entries_.end())
+ return;
+
+ it->second->set_value(NULL);
+}
+
+} // namespace breakpad
diff --git a/components/crash/content/app/crash_keys_win.h b/components/crash/content/app/crash_keys_win.h
new file mode 100644
index 0000000..870de77
--- /dev/null
+++ b/components/crash/content/app/crash_keys_win.h
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASH_KEYS_WIN_H_
+#define COMPONENTS_CRASH_CONTENT_APP_CRASH_KEYS_WIN_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "breakpad/src/client/windows/common/ipc_protocol.h"
+#include "breakpad/src/client/windows/handler/exception_handler.h"
+
+
+namespace base {
+class CommandLine;
+} // namespace base
+
+namespace crash_reporter {
+class CrashReporterClient;
+}
+
+namespace breakpad {
+
+// Manages the breakpad key/value pair stash, there may only be one instance
+// of this class per process at one time.
+class CrashKeysWin {
+ public:
+ CrashKeysWin();
+ ~CrashKeysWin();
+
+ // May only be called once.
+ // |exe_path| is the path to the executable running, which may be used
+ // to figure out whether this is a user or system install.
+ // |type| is the process type, or mode this process is running in e.g.
+ // something like "browser" or "renderer".
+ // |profile_type| is a string describing the kind of the user's Windows
+ // profile, e.g. "mandatory", or "roaming" or similar.
+ // |cmd_line| is the current process' command line consulted for explicit
+ // crash reporting flags.
+ // |crash_client| is consulted for crash reporting settings.
+ google_breakpad::CustomClientInfo* GetCustomInfo(
+ const std::wstring& exe_path,
+ const std::wstring& type,
+ const std::wstring& profile_type,
+ base::CommandLine* cmd_line,
+ crash_reporter::CrashReporterClient* crash_client);
+
+ void SetCrashKeyValue(const std::wstring& key, const std::wstring& value);
+ void ClearCrashKeyValue(const std::wstring& key);
+
+ const std::vector<google_breakpad::CustomInfoEntry>& custom_info_entries()
+ const {
+ return custom_entries_;
+ }
+
+ static CrashKeysWin* keeper() { return keeper_; }
+
+ private:
+ // One-time initialization of private key/value pairs.
+ void SetPluginPath(const std::wstring& path);
+ void SetBreakpadDumpPath(crash_reporter::CrashReporterClient* crash_client);
+
+ // Must not be resized after GetCustomInfo is invoked.
+ std::vector<google_breakpad::CustomInfoEntry> custom_entries_;
+
+ typedef std::map<std::wstring, google_breakpad::CustomInfoEntry*>
+ DynamicEntriesMap;
+ base::Lock lock_;
+ // Keeps track of the next index for a new dynamic entry.
+ size_t dynamic_keys_offset_; // Under lock_.
+ // Maintains key->entry information for dynamic key/value entries
+ // in custom_entries_.
+ DynamicEntriesMap dynamic_entries_; // Under lock_.
+
+ // Stores the sole instance of this class allowed per process.
+ static CrashKeysWin* keeper_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashKeysWin);
+};
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_CRASH_KEYS_WIN_H_
diff --git a/components/crash/content/app/crash_keys_win_unittest.cc b/components/crash/content/app/crash_keys_win_unittest.cc
new file mode 100644
index 0000000..19d3942
--- /dev/null
+++ b/components/crash/content/app/crash_keys_win_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/app/crash_keys_win.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/strings/stringprintf.h"
+#include "components/crash/content/app/crash_reporter_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace breakpad {
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+
+class MockCrashReporterClient : public crash_reporter::CrashReporterClient {
+ public:
+ MOCK_METHOD1(GetAlternativeCrashDumpLocation,
+ bool(base::FilePath* crash_dir));
+ MOCK_METHOD5(GetProductNameAndVersion, void(const base::FilePath& exe_path,
+ base::string16* product_name,
+ base::string16* version,
+ base::string16* special_build,
+ base::string16* channel_name));
+ MOCK_METHOD3(ShouldShowRestartDialog, bool(base::string16* title,
+ base::string16* message,
+ bool* is_rtl_locale));
+ MOCK_METHOD0(AboutToRestart, bool());
+ MOCK_METHOD1(GetDeferredUploadsSupported, bool(bool is_per_user_install));
+ MOCK_METHOD1(GetIsPerUserInstall, bool(const base::FilePath& exe_path));
+ MOCK_METHOD1(GetShouldDumpLargerDumps, bool(bool is_per_user_install));
+ MOCK_METHOD0(GetResultCodeRespawnFailed, int());
+ MOCK_METHOD0(InitBrowserCrashDumpsRegKey, void());
+ MOCK_METHOD1(RecordCrashDumpAttempt, void(bool is_real_crash));
+
+ MOCK_METHOD2(GetProductNameAndVersion, void(std::string* product_name,
+ std::string* version));
+ MOCK_METHOD0(GetReporterLogFilename, base::FilePath());
+ MOCK_METHOD1(GetCrashDumpLocation, bool(base::FilePath* crash_dir));
+ MOCK_METHOD0(RegisterCrashKeys, size_t());
+ MOCK_METHOD0(IsRunningUnattended, bool());
+ MOCK_METHOD0(GetCollectStatsConsent, bool());
+ MOCK_METHOD1(ReportingIsEnforcedByPolicy, bool(bool* breakpad_enabled));
+ MOCK_METHOD0(GetAndroidMinidumpDescriptor, int());
+ MOCK_METHOD1(EnableBreakpadForProcess, bool(const std::string& process_type));
+};
+
+class CrashKeysWinTest : public testing::Test {
+ public:
+
+ size_t CountKeyValueOccurences(
+ const google_breakpad::CustomClientInfo* client_info,
+ const wchar_t* key, const wchar_t* value);
+
+ protected:
+ testing::StrictMock<MockCrashReporterClient> crash_client_;
+};
+
+size_t CrashKeysWinTest::CountKeyValueOccurences(
+ const google_breakpad::CustomClientInfo* client_info,
+ const wchar_t* key, const wchar_t* value) {
+ size_t occurrences = 0;
+ for (size_t i = 0; i < client_info->count; ++i) {
+ if (wcscmp(client_info->entries[i].name, key) == 0 &&
+ wcscmp(client_info->entries[i].value, value) == 0) {
+ ++occurrences;
+ }
+ }
+
+ return occurrences;
+}
+
+TEST_F(CrashKeysWinTest, RecordsSelf) {
+ ASSERT_EQ(static_cast<CrashKeysWin*>(NULL), CrashKeysWin::keeper());
+
+ {
+ CrashKeysWin crash_keys;
+
+ ASSERT_EQ(&crash_keys, CrashKeysWin::keeper());
+ }
+
+ ASSERT_EQ(static_cast<CrashKeysWin*>(NULL), CrashKeysWin::keeper());
+}
+
+// Tests the crash keys set up for the most common official build consumer
+// scenario. No policy controls, not running unattended and no explicit
+// switches.
+TEST_F(CrashKeysWinTest, OfficialLikeKeys) {
+ CrashKeysWin crash_keys;
+
+ const base::FilePath kExePath(L"C:\\temp\\exe_path.exe");
+ // The exe path ought to get passed through to the breakpad client.
+ EXPECT_CALL(crash_client_, GetProductNameAndVersion(kExePath, _, _, _, _))
+ .WillRepeatedly(DoAll(
+ SetArgPointee<1>(L"SomeProdName"),
+ SetArgPointee<2>(L"1.2.3.4"),
+ SetArgPointee<3>(L""),
+ SetArgPointee<4>(L"-devm")));
+
+ EXPECT_CALL(crash_client_, GetAlternativeCrashDumpLocation(_))
+ .WillRepeatedly(DoAll(
+ SetArgPointee<0>(base::FilePath(L"C:\\temp")),
+ Return(false)));
+
+ EXPECT_CALL(crash_client_, ReportingIsEnforcedByPolicy(_))
+ .WillRepeatedly(Return(false));
+
+ EXPECT_CALL(crash_client_, IsRunningUnattended())
+ .WillRepeatedly(Return(false));
+
+ // Provide an empty command line.
+ base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM);
+ google_breakpad::CustomClientInfo* info =
+ crash_keys.GetCustomInfo(kExePath.value(),
+ L"made_up_type",
+ L"temporary",
+ &cmd_line,
+ &crash_client_);
+
+ ASSERT_TRUE(info != NULL);
+ ASSERT_TRUE(info->entries != NULL);
+
+ // We expect 7 fixed keys and a "freeboard" of 256 keys for dynamic entries.
+ EXPECT_EQ(256U + 7U, info->count);
+
+ EXPECT_EQ(1, CountKeyValueOccurences(info, L"ver", L"1.2.3.4"));
+ EXPECT_EQ(1, CountKeyValueOccurences(info, L"prod", L"SomeProdName"));
+ EXPECT_EQ(1, CountKeyValueOccurences(info, L"plat", L"Win32"));
+ EXPECT_EQ(1, CountKeyValueOccurences(info, L"ptype", L"made_up_type"));
+ std::wstring pid_str(base::StringPrintf(L"%d", ::GetCurrentProcessId()));
+ EXPECT_EQ(1, CountKeyValueOccurences(info, L"pid", pid_str.c_str()));
+ EXPECT_EQ(1, CountKeyValueOccurences(info, L"channel", L"-devm"));
+ EXPECT_EQ(1, CountKeyValueOccurences(info, L"profile-type", L"temporary"));
+ EXPECT_EQ(256, CountKeyValueOccurences(info, L"unspecified-crash-key", L""));
+}
+
+} // namespace breakpad
diff --git a/components/crash/content/app/crash_reporter_client.cc b/components/crash/content/app/crash_reporter_client.cc
new file mode 100644
index 0000000..66388e2
--- /dev/null
+++ b/components/crash/content/app/crash_reporter_client.cc
@@ -0,0 +1,148 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/app/crash_reporter_client.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+
+namespace crash_reporter {
+
+namespace {
+
+CrashReporterClient* g_client = NULL;
+
+} // namespace
+
+void SetCrashReporterClient(CrashReporterClient* client) {
+ g_client = client;
+}
+
+CrashReporterClient* GetCrashReporterClient() {
+ DCHECK(g_client);
+ return g_client;
+}
+
+CrashReporterClient::CrashReporterClient() {}
+CrashReporterClient::~CrashReporterClient() {}
+
+#if !defined(OS_MACOSX)
+void CrashReporterClient::SetCrashReporterClientIdFromGUID(
+ const std::string& client_guid) {
+}
+#endif
+
+#if defined(OS_WIN)
+bool CrashReporterClient::GetAlternativeCrashDumpLocation(
+ base::FilePath* crash_dir) {
+ return false;
+}
+
+void CrashReporterClient::GetProductNameAndVersion(
+ const base::FilePath& exe_path,
+ base::string16* product_name,
+ base::string16* version,
+ base::string16* special_build,
+ base::string16* channel_name) {
+}
+
+bool CrashReporterClient::ShouldShowRestartDialog(base::string16* title,
+ base::string16* message,
+ bool* is_rtl_locale) {
+ return false;
+}
+
+bool CrashReporterClient::AboutToRestart() {
+ return false;
+}
+
+bool CrashReporterClient::GetDeferredUploadsSupported(bool is_per_usr_install) {
+ return false;
+}
+
+bool CrashReporterClient::GetIsPerUserInstall(const base::FilePath& exe_path) {
+ return true;
+}
+
+bool CrashReporterClient::GetShouldDumpLargerDumps(bool is_per_user_install) {
+ return false;
+}
+
+int CrashReporterClient::GetResultCodeRespawnFailed() {
+ return 0;
+}
+
+void CrashReporterClient::InitBrowserCrashDumpsRegKey() {
+}
+
+void CrashReporterClient::RecordCrashDumpAttempt(bool is_real_crash) {
+}
+
+void CrashReporterClient::RecordCrashDumpAttemptResult(bool is_real_crash,
+ bool succeeded) {
+}
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
+void CrashReporterClient::GetProductNameAndVersion(const char** product_name,
+ const char** version) {
+}
+
+base::FilePath CrashReporterClient::GetReporterLogFilename() {
+ return base::FilePath();
+}
+
+bool CrashReporterClient::HandleCrashDump(const char* crashdump_filename) {
+ return false;
+}
+#endif
+
+bool CrashReporterClient::GetCrashDumpLocation(base::FilePath* crash_dir) {
+ return false;
+}
+
+size_t CrashReporterClient::RegisterCrashKeys() {
+ return 0;
+}
+
+bool CrashReporterClient::IsRunningUnattended() {
+ return true;
+}
+
+bool CrashReporterClient::GetCollectStatsConsent() {
+ return false;
+}
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+bool CrashReporterClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) {
+ return false;
+}
+#endif
+
+#if defined(OS_ANDROID)
+int CrashReporterClient::GetAndroidMinidumpDescriptor() {
+ return 0;
+}
+
+bool CrashReporterClient::ShouldEnableBreakpadMicrodumps() {
+// Always enable microdumps on Android when stripping unwind tables. Rationale:
+// when unwind tables are stripped out (to save binary size) the stack traces
+// produced locally in the case of a crash / CHECK are meaningless. In order to
+// provide meaningful development diagnostics (and keep the binary size savings)
+// on Android we attach a secondary crash handler which serializes a reduced
+// form of logcat on the console.
+#if defined(NO_UNWIND_TABLES)
+ return true;
+#else
+ return false;
+#endif
+}
+#endif
+
+bool CrashReporterClient::EnableBreakpadForProcess(
+ const std::string& process_type) {
+ return false;
+}
+
+} // namespace crash_reporter
diff --git a/components/crash/content/app/crash_reporter_client.h b/components/crash/content/app/crash_reporter_client.h
new file mode 100644
index 0000000..4a96954
--- /dev/null
+++ b/components/crash/content/app/crash_reporter_client.h
@@ -0,0 +1,158 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASH_REPORTER_CLIENT_H_
+#define COMPONENTS_CRASH_CONTENT_APP_CRASH_REPORTER_CLIENT_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+
+namespace base {
+class FilePath;
+}
+
+#if defined(OS_MACOSX)
+// We don't want to directly include
+// breakpad/src/client/mac/Framework/Breakpad.h here, so we repeat the
+// definition of BreakpadRef.
+//
+// On Mac, when compiling without breakpad support, a stub implementation is
+// compiled in. Not having any includes of the breakpad library allows for
+// reusing this header for the stub.
+typedef void* BreakpadRef;
+#endif
+
+namespace crash_reporter {
+
+class CrashReporterClient;
+
+// Setter and getter for the client. The client should be set early, before any
+// crash reporter code is called, and should stay alive throughout the entire
+// runtime.
+void SetCrashReporterClient(CrashReporterClient* client);
+
+#if defined(CRASH_IMPLEMENTATION)
+// The components's embedder API should only be used by the component.
+CrashReporterClient* GetCrashReporterClient();
+#endif
+
+// Interface that the embedder implements.
+class CrashReporterClient {
+ public:
+ CrashReporterClient();
+ virtual ~CrashReporterClient();
+
+#if !defined(OS_MACOSX)
+ // Sets the crash reporting client ID, a unique identifier for the client
+ // that is sending crash reports. After it is set, it should not be changed.
+ // |client_guid| may either be a full GUID or a GUID that was already stripped
+ // from its dashes.
+ //
+ // On Mac OS X, this is the responsibility of Crashpad, and can not be set
+ // directly by the client.
+ virtual void SetCrashReporterClientIdFromGUID(const std::string& client_guid);
+#endif
+
+#if defined(OS_WIN)
+ // Returns true if an alternative location to store the minidump files was
+ // specified. Returns true if |crash_dir| was set.
+ virtual bool GetAlternativeCrashDumpLocation(base::FilePath* crash_dir);
+
+ // Returns a textual description of the product type and version to include
+ // in the crash report.
+ virtual void GetProductNameAndVersion(const base::FilePath& exe_path,
+ base::string16* product_name,
+ base::string16* version,
+ base::string16* special_build,
+ base::string16* channel_name);
+
+ // Returns true if a restart dialog should be displayed. In that case,
+ // |message| and |title| are set to a message to display in a dialog box with
+ // the given title before restarting, and |is_rtl_locale| indicates whether
+ // to display the text as RTL.
+ virtual bool ShouldShowRestartDialog(base::string16* title,
+ base::string16* message,
+ bool* is_rtl_locale);
+
+ // Returns true if it is ok to restart the application. Invoked right before
+ // restarting after a crash.
+ virtual bool AboutToRestart();
+
+ // Returns true if the crash report uploader supports deferred uploads.
+ virtual bool GetDeferredUploadsSupported(bool is_per_user_install);
+
+ // Returns true if the running binary is a per-user installation.
+ virtual bool GetIsPerUserInstall(const base::FilePath& exe_path);
+
+ // Returns true if larger crash dumps should be dumped.
+ virtual bool GetShouldDumpLargerDumps(bool is_per_user_install);
+
+ // Returns the result code to return when breakpad failed to respawn a
+ // crashed process.
+ virtual int GetResultCodeRespawnFailed();
+
+ // Invoked when initializing the crash reporter in the browser process.
+ virtual void InitBrowserCrashDumpsRegKey();
+
+ // Invoked before attempting to write a minidump.
+ virtual void RecordCrashDumpAttempt(bool is_real_crash);
+
+ // Invoked with the results of a minidump attempt.
+ virtual void RecordCrashDumpAttemptResult(bool is_real_crash, bool succeeded);
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
+ // Returns a textual description of the product type and version to include
+ // in the crash report. Neither out parameter should be set to NULL.
+ virtual void GetProductNameAndVersion(const char** product_name,
+ const char** version);
+
+ virtual base::FilePath GetReporterLogFilename();
+
+ // Custom crash minidump handler after the minidump is generated.
+ // Returns true if the minidump is handled (client); otherwise, return false
+ // to fallback to default handler.
+ // WARNING: this handler runs in a compromised context. It may not call into
+ // libc nor allocate memory normally.
+ virtual bool HandleCrashDump(const char* crashdump_filename);
+#endif
+
+ // The location where minidump files should be written. Returns true if
+ // |crash_dir| was set.
+ virtual bool GetCrashDumpLocation(base::FilePath* crash_dir);
+
+ // Register all of the potential crash keys that can be sent to the crash
+ // reporting server. Returns the size of the union of all keys.
+ virtual size_t RegisterCrashKeys();
+
+ // Returns true if running in unattended mode (for automated testing).
+ virtual bool IsRunningUnattended();
+
+ // Returns true if the user has given consent to collect stats.
+ virtual bool GetCollectStatsConsent();
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ // Returns true if crash reporting is enforced via management policies. In
+ // that case, |breakpad_enabled| is set to the value enforced by policies.
+ virtual bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled);
+#endif
+
+#if defined(OS_ANDROID)
+ // Returns the descriptor key of the android minidump global descriptor.
+ virtual int GetAndroidMinidumpDescriptor();
+
+ // Returns true if breakpad microdumps should be enabled. This orthogonal to
+ // the standard minidump uploader (which depends on the user consent).
+ virtual bool ShouldEnableBreakpadMicrodumps();
+#endif
+
+ // Returns true if breakpad should run in the given process type.
+ virtual bool EnableBreakpadForProcess(const std::string& process_type);
+};
+
+} // namespace crash_reporter
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_CRASH_REPORTER_CLIENT_H_
diff --git a/components/crash/content/app/crashpad_mac.h b/components/crash/content/app/crashpad_mac.h
new file mode 100644
index 0000000..b46cce5
--- /dev/null
+++ b/components/crash/content/app/crashpad_mac.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_MAC_H_
+#define COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_MAC_H_
+
+#include <time.h>
+
+#include <string>
+#include <vector>
+
+namespace crash_reporter {
+
+// Initializes Crashpad in a way that is appropriate for process_type. If
+// process_type is empty, initializes Crashpad for the browser process, which
+// starts crashpad_handler and sets it as the exception handler. Other process
+// types inherit this exception handler from the browser, but still need to
+// perform additional initialization.
+void InitializeCrashpad(const std::string& process_type);
+
+// Enables or disables crash report upload. This is a property of the Crashpad
+// database. In a newly-created database, uploads will be disabled. This
+// function only has an effect when called in the browser process. Its effect is
+// immediate and applies to all other process types, including processes that
+// are already running.
+void SetUploadsEnabled(bool enabled);
+
+// Determines whether uploads are enabled or disabled. This information is only
+// available in the browser process.
+bool GetUploadsEnabled();
+
+struct UploadedReport {
+ std::string local_id;
+ std::string remote_id;
+ time_t creation_time;
+};
+
+// Obtains a list of reports uploaded to the collection server. This function
+// only operates when called in the browser process. All reports in the Crashpad
+// database that have been successfully uploaded will be included in this list.
+// The list will be sorted in descending order by report creation time (newest
+// reports first).
+//
+// TODO(mark): The about:crashes UI expects to show only uploaded reports. If it
+// is ever enhanced to work well with un-uploaded reports, those should be
+// returned as well. Un-uploaded reports may have a pending upload, may have
+// experienced upload failure, or may have been collected while uploads were
+// disabled.
+void GetUploadedReports(std::vector<UploadedReport>* uploaded_reports);
+
+} // namespace crash_reporter
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_MAC_H_
diff --git a/components/crash/content/app/crashpad_mac.mm b/components/crash/content/app/crashpad_mac.mm
new file mode 100644
index 0000000..1082db4
--- /dev/null
+++ b/components/crash/content/app/crashpad_mac.mm
@@ -0,0 +1,251 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/app/crashpad_mac.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/crash/content/app/crash_reporter_client.h"
+#include "third_party/crashpad/crashpad/client/crash_report_database.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#include "third_party/crashpad/crashpad/client/crashpad_info.h"
+#include "third_party/crashpad/crashpad/client/settings.h"
+#include "third_party/crashpad/crashpad/client/simple_string_dictionary.h"
+#include "third_party/crashpad/crashpad/client/simulate_crash.h"
+
+namespace crash_reporter {
+
+namespace {
+
+crashpad::SimpleStringDictionary* g_simple_string_dictionary;
+crashpad::CrashReportDatabase* g_database;
+
+void SetCrashKeyValue(const base::StringPiece& key,
+ const base::StringPiece& value) {
+ g_simple_string_dictionary->SetKeyValue(key.data(), value.data());
+}
+
+void ClearCrashKey(const base::StringPiece& key) {
+ g_simple_string_dictionary->RemoveKey(key.data());
+}
+
+bool LogMessageHandler(int severity,
+ const char* file,
+ int line,
+ size_t message_start,
+ const std::string& string) {
+ // Only handle FATAL.
+ if (severity != logging::LOG_FATAL) {
+ return false;
+ }
+
+ // In case of an out-of-memory condition, this code could be reentered when
+ // constructing and storing the key. Using a static is not thread-safe, but if
+ // multiple threads are in the process of a fatal crash at the same time, this
+ // should work.
+ static bool guarded = false;
+ if (guarded) {
+ return false;
+ }
+ base::AutoReset<bool> guard(&guarded, true);
+
+ // Only log last path component. This matches logging.cc.
+ if (file) {
+ const char* slash = strrchr(file, '/');
+ if (slash) {
+ file = slash + 1;
+ }
+ }
+
+ std::string message = base::StringPrintf("%s:%d: %s", file, line,
+ string.c_str() + message_start);
+ SetCrashKeyValue("LOG_FATAL", message);
+
+ // Rather than including the code to force the crash here, allow the caller to
+ // do it.
+ return false;
+}
+
+void DumpWithoutCrashing() {
+ CRASHPAD_SIMULATE_CRASH();
+}
+
+} // namespace
+
+void InitializeCrashpad(const std::string& process_type) {
+ static bool initialized = false;
+ DCHECK(!initialized);
+ initialized = true;
+
+ const bool browser_process = process_type.empty();
+ CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
+
+ base::FilePath database_path; // Only valid in the browser process.
+
+ if (browser_process) {
+ @autoreleasepool {
+ base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath();
+ base::FilePath handler_path =
+ framework_bundle_path.Append("Helpers").Append("crashpad_handler");
+
+ // Is there a way to recover if this fails?
+ crash_reporter_client->GetCrashDumpLocation(&database_path);
+
+ // TODO(mark): Reading the Breakpad keys is temporary and transitional. At
+ // the very least, they should be renamed to Crashpad. For the time being,
+ // this isn't the worst thing: Crashpad is still uploading to a
+ // Breakpad-type server, after all.
+ NSBundle* framework_bundle = base::mac::FrameworkBundle();
+ NSString* product = base::mac::ObjCCast<NSString>(
+ [framework_bundle objectForInfoDictionaryKey:@"BreakpadProduct"]);
+ NSString* version = base::mac::ObjCCast<NSString>(
+ [framework_bundle objectForInfoDictionaryKey:@"BreakpadVersion"]);
+ NSString* url_ns = base::mac::ObjCCast<NSString>(
+ [framework_bundle objectForInfoDictionaryKey:@"BreakpadURL"]);
+
+ std::string url = base::SysNSStringToUTF8(url_ns);
+
+ std::map<std::string, std::string> process_annotations;
+ process_annotations["prod"] = base::SysNSStringToUTF8(product);
+ process_annotations["ver"] = base::SysNSStringToUTF8(version);
+ process_annotations["plat"] = std::string("OS X");
+
+ crashpad::CrashpadClient crashpad_client;
+ if (crashpad_client.StartHandler(handler_path, database_path, url,
+ process_annotations,
+ std::vector<std::string>())) {
+ crashpad_client.UseHandler();
+ }
+ } // @autoreleasepool
+ }
+
+ crashpad::CrashpadInfo* crashpad_info =
+ crashpad::CrashpadInfo::GetCrashpadInfo();
+
+#if defined(NDEBUG)
+ const bool is_debug_build = false;
+#else
+ const bool is_debug_build = true;
+#endif
+
+ // Disable forwarding to the system's crash reporter in processes other than
+ // the browser process. For the browser, the system's crash reporter presents
+ // the crash UI to the user, so it's desirable there. Additionally, having
+ // crash reports appear in ~/Library/Logs/DiagnosticReports provides a
+ // fallback. Forwarding is turned off for debug-mode builds even for the
+ // browser process, because the system's crash reporter can take a very long
+ // time to chew on symbols.
+ if (!browser_process || is_debug_build) {
+ crashpad_info->set_system_crash_reporter_forwarding(
+ crashpad::TriState::kDisabled);
+ }
+
+ g_simple_string_dictionary = new crashpad::SimpleStringDictionary();
+ crashpad_info->set_simple_annotations(g_simple_string_dictionary);
+
+ base::debug::SetCrashKeyReportingFunctions(SetCrashKeyValue, ClearCrashKey);
+ crash_reporter_client->RegisterCrashKeys();
+
+ SetCrashKeyValue("ptype", browser_process ? base::StringPiece("browser")
+ : base::StringPiece(process_type));
+ SetCrashKeyValue("pid", base::IntToString(getpid()));
+
+ logging::SetLogMessageHandler(LogMessageHandler);
+
+ // If clients called CRASHPAD_SIMULATE_CRASH() instead of
+ // base::debug::DumpWithoutCrashing(), these dumps would appear as crashes in
+ // the correct function, at the correct file and line. This would be
+ // preferable to having all occurrences show up in DumpWithoutCrashing() at
+ // the same file and line.
+ base::debug::SetDumpWithoutCrashingFunction(DumpWithoutCrashing);
+
+ if (browser_process) {
+ g_database =
+ crashpad::CrashReportDatabase::Initialize(database_path).release();
+
+ bool enable_uploads = false;
+ if (!crash_reporter_client->ReportingIsEnforcedByPolicy(&enable_uploads)) {
+ enable_uploads = crash_reporter_client->GetCollectStatsConsent() &&
+ !crash_reporter_client->IsRunningUnattended();
+ // Breakpad provided a --disable-breakpad switch to disable crash dumping
+ // (not just uploading) here. Crashpad doesn't need it: dumping is enabled
+ // unconditionally and uploading is gated on consent, which tests/bots
+ // shouldn't have. As a precaution, we also force disable uploading on
+ // bots even if consent is present.
+ }
+
+ SetUploadsEnabled(enable_uploads);
+ }
+}
+
+void SetUploadsEnabled(bool enable_uploads) {
+ if (g_database) {
+ crashpad::Settings* settings = g_database->GetSettings();
+ settings->SetUploadsEnabled(enable_uploads);
+ }
+}
+
+bool GetUploadsEnabled() {
+ if (g_database) {
+ crashpad::Settings* settings = g_database->GetSettings();
+ bool enable_uploads;
+ if (settings->GetUploadsEnabled(&enable_uploads)) {
+ return enable_uploads;
+ }
+ }
+
+ return false;
+}
+
+void GetUploadedReports(std::vector<UploadedReport>* uploaded_reports) {
+ uploaded_reports->clear();
+
+ if (!g_database) {
+ return;
+ }
+
+ std::vector<crashpad::CrashReportDatabase::Report> completed_reports;
+ crashpad::CrashReportDatabase::OperationStatus status =
+ g_database->GetCompletedReports(&completed_reports);
+ if (status != crashpad::CrashReportDatabase::kNoError) {
+ return;
+ }
+
+ for (const crashpad::CrashReportDatabase::Report& completed_report :
+ completed_reports) {
+ if (completed_report.uploaded) {
+ UploadedReport uploaded_report;
+ uploaded_report.local_id = completed_report.uuid.ToString();
+ uploaded_report.remote_id = completed_report.id;
+ uploaded_report.creation_time = completed_report.creation_time;
+
+ uploaded_reports->push_back(uploaded_report);
+ }
+ }
+
+ struct {
+ bool operator()(const UploadedReport& a, const UploadedReport& b) {
+ return a.creation_time >= b.creation_time;
+ }
+ } sort_by_time;
+ std::sort(uploaded_reports->begin(), uploaded_reports->end(), sort_by_time);
+}
+
+} // namespace crash_reporter
diff --git a/components/crash/content/app/hard_error_handler_win.cc b/components/crash/content/app/hard_error_handler_win.cc
new file mode 100644
index 0000000..a310666
--- /dev/null
+++ b/components/crash/content/app/hard_error_handler_win.cc
@@ -0,0 +1,112 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/app/hard_error_handler_win.h"
+
+#include <DelayIMP.h>
+#include <winternl.h>
+
+#include "base/basictypes.h"
+#include "base/strings/string_util.h"
+#include "components/crash/content/app/crash_reporter_client.h"
+
+namespace breakpad {
+
+using crash_reporter::GetCrashReporterClient;
+
+namespace {
+const DWORD kExceptionModuleNotFound = VcppException(ERROR_SEVERITY_ERROR,
+ ERROR_MOD_NOT_FOUND);
+const DWORD kExceptionEntryPtNotFound = VcppException(ERROR_SEVERITY_ERROR,
+ ERROR_PROC_NOT_FOUND);
+// This is defined in <ntstatus.h> but we can't include this file here.
+const DWORD FACILITY_GRAPHICS_KERNEL = 0x1E;
+const DWORD NT_STATUS_ENTRYPOINT_NOT_FOUND = 0xC0000139;
+const DWORD NT_STATUS_DLL_NOT_FOUND = 0xC0000135;
+
+// We assume that exception codes are NT_STATUS codes.
+DWORD FacilityFromException(DWORD exception_code) {
+ return (exception_code >> 16) & 0x0FFF;
+}
+
+// This is not a generic function. It only works with some |nt_status| values.
+// Check the strings here http://msdn.microsoft.com/en-us/library/cc704588.aspx
+// before attempting to use this function.
+void RaiseHardErrorMsg(long nt_status, const std::string& p1,
+ const std::string& p2) {
+ // If headless just exit silently.
+ if (GetCrashReporterClient()->IsRunningUnattended())
+ return;
+
+ HMODULE ntdll = ::GetModuleHandleA("NTDLL.DLL");
+ wchar_t* msg_template = NULL;
+ size_t count = ::FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_HMODULE,
+ ntdll,
+ nt_status,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ reinterpret_cast<wchar_t*>(&msg_template),
+ 0,
+ NULL);
+
+ if (!count)
+ return;
+ count += p1.size() + p2.size() + 1;
+ base::string16 message;
+ ::wsprintf(base::WriteInto(&message, count), msg_template,
+ p1.c_str(), p2.c_str());
+ // The MB_SERVICE_NOTIFICATION causes this message to be displayed by
+ // csrss. This means that we are not creating windows or pumping WM messages
+ // in this process.
+ ::MessageBox(NULL, message.c_str(),
+ L"chrome.exe",
+ MB_OK | MB_SERVICE_NOTIFICATION);
+ ::LocalFree(msg_template);
+}
+
+void ModuleNotFoundHardError(const EXCEPTION_RECORD* ex_record) {
+ DelayLoadInfo* dli = reinterpret_cast<DelayLoadInfo*>(
+ ex_record->ExceptionInformation[0]);
+ if (!dli->szDll)
+ return;
+ RaiseHardErrorMsg(NT_STATUS_DLL_NOT_FOUND, dli->szDll, std::string());
+}
+
+void EntryPointNotFoundHardError(const EXCEPTION_RECORD* ex_record) {
+ DelayLoadInfo* dli = reinterpret_cast<DelayLoadInfo*>(
+ ex_record->ExceptionInformation[0]);
+ if (!dli->dlp.fImportByName)
+ return;
+ if (!dli->dlp.szProcName)
+ return;
+ if (!dli->szDll)
+ return;
+ RaiseHardErrorMsg(NT_STATUS_ENTRYPOINT_NOT_FOUND,
+ dli->dlp.szProcName, dli->szDll);
+}
+
+} // namespace
+
+bool HardErrorHandler(EXCEPTION_POINTERS* ex_info) {
+ if (!ex_info)
+ return false;
+ if (!ex_info->ExceptionRecord)
+ return false;
+
+ long exception = ex_info->ExceptionRecord->ExceptionCode;
+ if (exception == kExceptionModuleNotFound) {
+ ModuleNotFoundHardError(ex_info->ExceptionRecord);
+ return true;
+ } else if (exception == kExceptionEntryPtNotFound) {
+ EntryPointNotFoundHardError(ex_info->ExceptionRecord);
+ return true;
+ } else if (FacilityFromException(exception) == FACILITY_GRAPHICS_KERNEL) {
+ RaiseHardErrorMsg(exception, std::string(), std::string());
+ return true;
+ }
+ return false;
+}
+
+} // namespace breakpad
diff --git a/components/crash/content/app/hard_error_handler_win.h b/components/crash/content/app/hard_error_handler_win.h
new file mode 100644
index 0000000..f1afc6b
--- /dev/null
+++ b/components/crash/content/app/hard_error_handler_win.h
@@ -0,0 +1,34 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_APP_HARD_ERROR_HANDLER_WIN_H_
+#define COMPONENTS_CRASH_CONTENT_APP_HARD_ERROR_HANDLER_WIN_H_
+
+#include <windows.h>
+
+namespace breakpad {
+
+// This function is in charge of displaying a dialog box that informs the
+// user of a fatal condition in chrome. It is meant to be called from
+// breakpad's unhandled exception handler after the crash dump has been
+// created. The return value will be true if we are to retry launching
+// chrome (and show the 'chrome has crashed' dialog) or to silently exit.
+//
+// This function only handles a few known exceptions, currently:
+// - Failure to load a delayload dll.
+// - Failure to bind to a delayloaded import.
+// - Fatal Graphics card failure (aura build only).
+//
+// If any of these conditions are encountered, a message box shown by
+// the operating system CSRSS process via NtRaiseHardError is invoked.
+// The wording and localization is up to the operating system.
+//
+// Do not call this function for memory related errors like heap corruption
+// or stack exahustion. This function assumes that memory allocations are
+// possible.
+bool HardErrorHandler(EXCEPTION_POINTERS* ex_info);
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_HARD_ERROR_HANDLER_WIN_H_
diff --git a/components/crash/content/browser/BUILD.gn b/components/crash/content/browser/BUILD.gn
new file mode 100644
index 0000000..1af0d89
--- /dev/null
+++ b/components/crash/content/browser/BUILD.gn
@@ -0,0 +1,38 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (is_android) {
+ import("//build/config/android/config.gni")
+}
+
+source_set("browser") {
+ sources = [
+ "crash_dump_manager_android.cc",
+ "crash_dump_manager_android.h",
+ ]
+
+ if (is_linux || is_android) {
+ set_sources_assignment_filter([])
+
+ # Want this file on both Linux and Android.
+ sources += [
+ "crash_handler_host_linux.cc",
+ "crash_handler_host_linux.h",
+ ]
+ }
+
+ deps = [
+ "//base",
+ "//components/crash/content/app",
+ "//content/public/browser",
+ "//content/public/common",
+ ]
+
+ # This is not in the GYP build but this target includes breakpad client
+ # headers, so add the dependency here.
+ if (is_posix && !is_ios) {
+ configs += [ "//breakpad:client_config" ]
+ public_configs = [ "//breakpad:client_config" ]
+ }
+}
diff --git a/components/crash/content/browser/DEPS b/components/crash/content/browser/DEPS
new file mode 100644
index 0000000..c24130e
--- /dev/null
+++ b/components/crash/content/browser/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/browser",
+ "+content/public/common",
+]
diff --git a/components/crash/content/browser/crash_dump_manager_android.cc b/components/crash/content/browser/crash_dump_manager_android.cc
new file mode 100644
index 0000000..62e77ad
--- /dev/null
+++ b/components/crash/content/browser/crash_dump_manager_android.cc
@@ -0,0 +1,174 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/browser/crash_dump_manager_android.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/posix/global_descriptors.h"
+#include "base/process/process.h"
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/file_descriptor_info.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+
+using content::BrowserThread;
+
+namespace breakpad {
+
+// static
+CrashDumpManager* CrashDumpManager::instance_ = NULL;
+
+// static
+CrashDumpManager* CrashDumpManager::GetInstance() {
+ CHECK(instance_);
+ return instance_;
+}
+
+CrashDumpManager::CrashDumpManager(const base::FilePath& crash_dump_dir)
+ : crash_dump_dir_(crash_dump_dir) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(!instance_);
+
+ instance_ = this;
+
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+ content::NotificationService::AllSources());
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
+ content::NotificationService::AllSources());
+
+ BrowserChildProcessObserver::Add(this);
+}
+
+CrashDumpManager::~CrashDumpManager() {
+ instance_ = NULL;
+
+ BrowserChildProcessObserver::Remove(this);
+}
+
+base::File CrashDumpManager::CreateMinidumpFile(int child_process_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ base::FilePath minidump_path;
+ if (!base::CreateTemporaryFile(&minidump_path))
+ return base::File();
+
+ // We need read permission as the minidump is generated in several phases
+ // and needs to be read at some point.
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_WRITE;
+ base::File minidump_file(minidump_path, flags);
+ if (!minidump_file.IsValid()) {
+ LOG(ERROR) << "Failed to create temporary file, crash won't be reported.";
+ return base::File();
+ }
+
+ {
+ base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_);
+ DCHECK(!ContainsKey(child_process_id_to_minidump_path_, child_process_id));
+ child_process_id_to_minidump_path_[child_process_id] = minidump_path;
+ }
+ return minidump_file.Pass();
+}
+
+// static
+void CrashDumpManager::ProcessMinidump(const base::FilePath& minidump_path,
+ base::ProcessHandle pid) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ CHECK(instance_);
+ int64 file_size = 0;
+ int r = base::GetFileSize(minidump_path, &file_size);
+ DCHECK(r) << "Failed to retrieve size for minidump "
+ << minidump_path.value();
+
+ if (file_size == 0) {
+ // Empty minidump, this process did not crash. Just remove the file.
+ r = base::DeleteFile(minidump_path, false);
+ DCHECK(r) << "Failed to delete temporary minidump file "
+ << minidump_path.value();
+ return;
+ }
+
+ // We are dealing with a valid minidump. Copy it to the crash report
+ // directory from where Java code will upload it later on.
+ if (instance_->crash_dump_dir_.empty()) {
+ NOTREACHED() << "Failed to retrieve the crash dump directory.";
+ return;
+ }
+ const uint64 rand = base::RandUint64();
+ const std::string filename =
+ base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d",
+ rand, pid);
+ base::FilePath dest_path = instance_->crash_dump_dir_.Append(filename);
+ r = base::Move(minidump_path, dest_path);
+ if (!r) {
+ LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value()
+ << " to " << dest_path.value();
+ base::DeleteFile(minidump_path, false);
+ return;
+ }
+ VLOG(1) << "Crash minidump successfully generated: " <<
+ instance_->crash_dump_dir_.Append(filename).value();
+}
+
+void CrashDumpManager::BrowserChildProcessHostDisconnected(
+ const content::ChildProcessData& data) {
+ OnChildExit(data.id, data.handle);
+}
+
+void CrashDumpManager::BrowserChildProcessCrashed(
+ const content::ChildProcessData& data,
+ int exit_code) {
+ OnChildExit(data.id, data.handle);
+}
+
+void CrashDumpManager::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
+ // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer
+ // process is cleanly shutdown. However, we need to fallthrough so that
+ // we close the minidump_fd we kept open.
+ case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
+ content::RenderProcessHost* rph =
+ content::Source<content::RenderProcessHost>(source).ptr();
+ OnChildExit(rph->GetID(), rph->GetHandle());
+ break;
+ }
+ default:
+ NOTREACHED();
+ return;
+ }
+}
+
+void CrashDumpManager::OnChildExit(int child_process_id,
+ base::ProcessHandle pid) {
+ base::FilePath minidump_path;
+ {
+ base::AutoLock auto_lock(child_process_id_to_minidump_path_lock_);
+ ChildProcessIDToMinidumpPath::iterator iter =
+ child_process_id_to_minidump_path_.find(child_process_id);
+ if (iter == child_process_id_to_minidump_path_.end()) {
+ // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a
+ // NOTIFICATION_RENDERER_PROCESS_CLOSED.
+ return;
+ }
+ minidump_path = iter->second;
+ child_process_id_to_minidump_path_.erase(iter);
+ }
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&CrashDumpManager::ProcessMinidump, minidump_path, pid));
+}
+
+} // namespace breakpad
diff --git a/components/crash/content/browser/crash_dump_manager_android.h b/components/crash/content/browser/crash_dump_manager_android.h
new file mode 100644
index 0000000..e3f2391
--- /dev/null
+++ b/components/crash/content/browser/crash_dump_manager_android.h
@@ -0,0 +1,85 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_
+
+#include <map>
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/process/process.h"
+#include "base/synchronization/lock.h"
+#include "content/public/browser/browser_child_process_observer.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+namespace content {
+class RenderProcessHost;
+}
+
+namespace breakpad {
+
+// This class manages the crash minidumps.
+// On Android, because of process isolation, each renderer process runs with a
+// different UID. As a result, we cannot generate the minidumps in the browser
+// (as the browser process does not have access to some system files for the
+// crashed process). So the minidump is generated in the renderer process.
+// Since the isolated process cannot open files, we provide it on creation with
+// a file descriptor where to write the minidump in the event of a crash.
+// This class creates these file descriptors and associates them with render
+// processes and take the appropriate action when the render process terminates.
+class CrashDumpManager : public content::BrowserChildProcessObserver,
+ public content::NotificationObserver {
+ public:
+ // The embedder should create a single instance of the CrashDumpManager.
+ static CrashDumpManager* GetInstance();
+
+ // Should be created on the UI thread.
+ explicit CrashDumpManager(const base::FilePath& crash_dump_dir);
+
+ ~CrashDumpManager() override;
+
+ // Returns a file that should be used to generate a minidump for the process
+ // |child_process_id|.
+ base::File CreateMinidumpFile(int child_process_id);
+
+ private:
+ typedef std::map<int, base::FilePath> ChildProcessIDToMinidumpPath;
+
+ static void ProcessMinidump(const base::FilePath& minidump_path,
+ base::ProcessHandle pid);
+
+ // content::BrowserChildProcessObserver implementation:
+ void BrowserChildProcessHostDisconnected(
+ const content::ChildProcessData& data) override;
+ void BrowserChildProcessCrashed(
+ const content::ChildProcessData& data,
+ int exit_code) override;
+
+ // NotificationObserver implementation:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // Called on child process exit (including crash).
+ void OnChildExit(int child_process_id, base::ProcessHandle pid);
+
+ content::NotificationRegistrar notification_registrar_;
+
+ // This map should only be accessed with its lock aquired as it is accessed
+ // from the PROCESS_LAUNCHER and UI threads.
+ base::Lock child_process_id_to_minidump_path_lock_;
+ ChildProcessIDToMinidumpPath child_process_id_to_minidump_path_;
+
+ base::FilePath crash_dump_dir_;
+
+ static CrashDumpManager* instance_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashDumpManager);
+};
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_
diff --git a/components/crash/content/browser/crash_handler_host_linux.cc b/components/crash/content/browser/crash_handler_host_linux.cc
new file mode 100644
index 0000000..a29a3ed
--- /dev/null
+++ b/components/crash/content/browser/crash_handler_host_linux.cc
@@ -0,0 +1,437 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/browser/crash_handler_host_linux.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/format_macros.h"
+#include "base/linux_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/rand_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread.h"
+#include "breakpad/src/client/linux/handler/exception_handler.h"
+#include "breakpad/src/client/linux/minidump_writer/linux_dumper.h"
+#include "breakpad/src/client/linux/minidump_writer/minidump_writer.h"
+#include "components/crash/content/app/breakpad_linux_impl.h"
+#include "content/public/browser/browser_thread.h"
+
+#if defined(OS_ANDROID) && !defined(__LP64__)
+#include <sys/linux-syscalls.h>
+
+#define SYS_read __NR_read
+#endif
+
+using content::BrowserThread;
+using google_breakpad::ExceptionHandler;
+
+namespace breakpad {
+
+namespace {
+
+const size_t kNumFDs = 1;
+// The length of the control message:
+const size_t kControlMsgSize =
+ CMSG_SPACE(kNumFDs * sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
+// The length of the regular payload:
+const size_t kCrashContextSize = sizeof(ExceptionHandler::CrashContext);
+
+// Handles the crash dump and frees the allocated BreakpadInfo struct.
+void CrashDumpTask(CrashHandlerHostLinux* handler,
+ scoped_ptr<BreakpadInfo> info) {
+ if (handler->IsShuttingDown() && info->upload) {
+ base::DeleteFile(base::FilePath(info->filename), false);
+#if defined(ADDRESS_SANITIZER)
+ base::DeleteFile(base::FilePath(info->log_filename), false);
+#endif
+ return;
+ }
+
+ HandleCrashDump(*info);
+ delete[] info->filename;
+#if defined(ADDRESS_SANITIZER)
+ delete[] info->log_filename;
+ delete[] info->asan_report_str;
+#endif
+ delete[] info->process_type;
+ delete[] info->distro;
+ delete info->crash_keys;
+}
+
+} // namespace
+
+// Since instances of CrashHandlerHostLinux are leaked, they are only destroyed
+// at the end of the processes lifetime, which is greater in span than the
+// lifetime of the IO message loop. Thus, all calls to base::Bind() use
+// non-refcounted pointers.
+
+CrashHandlerHostLinux::CrashHandlerHostLinux(const std::string& process_type,
+ const base::FilePath& dumps_path,
+ bool upload)
+ : process_type_(process_type),
+ dumps_path_(dumps_path),
+ upload_(upload),
+ shutting_down_(false),
+ worker_pool_token_(BrowserThread::GetBlockingPool()->GetSequenceToken()) {
+ int fds[2];
+ // We use SOCK_SEQPACKET rather than SOCK_DGRAM to prevent the process from
+ // sending datagrams to other sockets on the system. The sandbox may prevent
+ // the process from calling socket() to create new sockets, but it'll still
+ // inherit some sockets. With PF_UNIX+SOCK_DGRAM, it can call sendmsg to send
+ // a datagram to any (abstract) socket on the same system. With
+ // SOCK_SEQPACKET, this is prevented.
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ static const int on = 1;
+
+ // Enable passcred on the server end of the socket
+ CHECK_EQ(0, setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)));
+
+ process_socket_ = fds[0];
+ browser_socket_ = fds[1];
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&CrashHandlerHostLinux::Init, base::Unretained(this)));
+}
+
+CrashHandlerHostLinux::~CrashHandlerHostLinux() {
+ close(process_socket_);
+ close(browser_socket_);
+}
+
+void CrashHandlerHostLinux::StartUploaderThread() {
+ uploader_thread_.reset(
+ new base::Thread(process_type_ + "_crash_uploader"));
+ uploader_thread_->Start();
+}
+
+void CrashHandlerHostLinux::Init() {
+ base::MessageLoopForIO* ml = base::MessageLoopForIO::current();
+ CHECK(ml->WatchFileDescriptor(
+ browser_socket_, true /* persistent */,
+ base::MessageLoopForIO::WATCH_READ,
+ &file_descriptor_watcher_, this));
+ ml->AddDestructionObserver(this);
+}
+
+void CrashHandlerHostLinux::OnFileCanWriteWithoutBlocking(int fd) {
+ NOTREACHED();
+}
+
+void CrashHandlerHostLinux::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK_EQ(browser_socket_, fd);
+
+ // A process has crashed and has signaled us by writing a datagram
+ // to the death signal socket. The datagram contains the crash context needed
+ // for writing the minidump as well as a file descriptor and a credentials
+ // block so that they can't lie about their pid.
+ //
+ // The message sender is in components/crash/content/app/breakpad_linux.cc.
+
+ struct msghdr msg = {0};
+ struct iovec iov[kCrashIovSize];
+
+ scoped_ptr<char[]> crash_context(new char[kCrashContextSize]);
+#if defined(ADDRESS_SANITIZER)
+ scoped_ptr<char[]> asan_report(new char[kMaxAsanReportSize + 1]);
+#endif
+
+ scoped_ptr<CrashKeyStorage> crash_keys(new CrashKeyStorage);
+ google_breakpad::SerializedNonAllocatingMap* serialized_crash_keys;
+ size_t crash_keys_size = crash_keys->Serialize(
+ const_cast<const google_breakpad::SerializedNonAllocatingMap**>(
+ &serialized_crash_keys));
+
+ char* tid_buf_addr = NULL;
+ int tid_fd = -1;
+ uint64_t uptime;
+ size_t oom_size;
+ char control[kControlMsgSize];
+ const ssize_t expected_msg_size =
+ kCrashContextSize +
+ sizeof(tid_buf_addr) + sizeof(tid_fd) +
+ sizeof(uptime) +
+#if defined(ADDRESS_SANITIZER)
+ kMaxAsanReportSize + 1 +
+#endif
+ sizeof(oom_size) +
+ crash_keys_size;
+ iov[0].iov_base = crash_context.get();
+ iov[0].iov_len = kCrashContextSize;
+ iov[1].iov_base = &tid_buf_addr;
+ iov[1].iov_len = sizeof(tid_buf_addr);
+ iov[2].iov_base = &tid_fd;
+ iov[2].iov_len = sizeof(tid_fd);
+ iov[3].iov_base = &uptime;
+ iov[3].iov_len = sizeof(uptime);
+ iov[4].iov_base = &oom_size;
+ iov[4].iov_len = sizeof(oom_size);
+ iov[5].iov_base = serialized_crash_keys;
+ iov[5].iov_len = crash_keys_size;
+#if !defined(ADDRESS_SANITIZER)
+ static_assert(5 == kCrashIovSize - 1, "kCrashIovSize should equal 6");
+#else
+ iov[6].iov_base = asan_report.get();
+ iov[6].iov_len = kMaxAsanReportSize + 1;
+ static_assert(6 == kCrashIovSize - 1, "kCrashIovSize should equal 7");
+#endif
+ msg.msg_iov = iov;
+ msg.msg_iovlen = kCrashIovSize;
+ msg.msg_control = control;
+ msg.msg_controllen = kControlMsgSize;
+
+ const ssize_t msg_size = HANDLE_EINTR(recvmsg(browser_socket_, &msg, 0));
+ if (msg_size < 0) {
+ LOG(ERROR) << "Error reading from death signal socket. Crash dumping"
+ << " is disabled."
+ << " msg_size:" << msg_size
+ << " errno:" << errno;
+ file_descriptor_watcher_.StopWatchingFileDescriptor();
+ return;
+ }
+ const bool bad_message = (msg_size != expected_msg_size ||
+ msg.msg_controllen != kControlMsgSize ||
+ msg.msg_flags & ~MSG_TRUNC);
+ base::ScopedFD signal_fd;
+ pid_t crashing_pid = -1;
+ if (msg.msg_controllen > 0) {
+ // Walk the control payload and extract the file descriptor and
+ // validated pid.
+ for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
+ hdr = CMSG_NXTHDR(&msg, hdr)) {
+ if (hdr->cmsg_level != SOL_SOCKET)
+ continue;
+ if (hdr->cmsg_type == SCM_RIGHTS) {
+ const size_t len = hdr->cmsg_len -
+ (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
+ DCHECK_EQ(0U, len % sizeof(int));
+ const size_t num_fds = len / sizeof(int);
+ if (num_fds != kNumFDs) {
+ // A nasty process could try and send us too many descriptors and
+ // force a leak.
+ LOG(ERROR) << "Death signal contained wrong number of descriptors;"
+ << " num_fds:" << num_fds;
+ for (size_t i = 0; i < num_fds; ++i)
+ close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
+ return;
+ }
+ DCHECK(!signal_fd.is_valid());
+ int fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
+ DCHECK_GE(fd, 0); // The kernel should never send a negative fd.
+ signal_fd.reset(fd);
+ } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
+ DCHECK_EQ(-1, crashing_pid);
+ const struct ucred *cred =
+ reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
+ crashing_pid = cred->pid;
+ }
+ }
+ }
+
+ if (bad_message) {
+ LOG(ERROR) << "Received death signal message with the wrong size;"
+ << " msg.msg_controllen:" << msg.msg_controllen
+ << " msg.msg_flags:" << msg.msg_flags
+ << " kCrashContextSize:" << kCrashContextSize
+ << " kControlMsgSize:" << kControlMsgSize;
+ return;
+ }
+ if (crashing_pid == -1 || !signal_fd.is_valid()) {
+ LOG(ERROR) << "Death signal message didn't contain all expected control"
+ << " messages";
+ return;
+ }
+
+ // The crashing TID set inside the compromised context via
+ // sys_gettid() in ExceptionHandler::HandleSignal might be wrong (if
+ // the kernel supports PID namespacing) and may need to be
+ // translated.
+ //
+ // We expect the crashing thread to be in sys_read(), waiting for us to
+ // write to |signal_fd|. Most newer kernels where we have the different pid
+ // namespaces also have /proc/[pid]/syscall, so we can look through
+ // |actual_crashing_pid|'s thread group and find the thread that's in the
+ // read syscall with the right arguments.
+
+ std::string expected_syscall_data;
+ // /proc/[pid]/syscall is formatted as follows:
+ // syscall_number arg1 ... arg6 sp pc
+ // but we just check syscall_number through arg3.
+ base::StringAppendF(&expected_syscall_data, "%d 0x%x %p 0x1 ",
+ SYS_read, tid_fd, tid_buf_addr);
+ bool syscall_supported = false;
+ pid_t crashing_tid =
+ base::FindThreadIDWithSyscall(crashing_pid,
+ expected_syscall_data,
+ &syscall_supported);
+ if (crashing_tid == -1) {
+ // We didn't find the thread we want. Maybe it didn't reach
+ // sys_read() yet or the thread went away. We'll just take a
+ // guess here and assume the crashing thread is the thread group
+ // leader. If procfs syscall is not supported by the kernel, then
+ // we assume the kernel also does not support TID namespacing and
+ // trust the TID passed by the crashing process.
+ LOG(WARNING) << "Could not translate tid - assuming crashing thread is "
+ "thread group leader; syscall_supported=" << syscall_supported;
+ crashing_tid = crashing_pid;
+ }
+
+ ExceptionHandler::CrashContext* bad_context =
+ reinterpret_cast<ExceptionHandler::CrashContext*>(crash_context.get());
+ bad_context->tid = crashing_tid;
+
+ scoped_ptr<BreakpadInfo> info(new BreakpadInfo);
+
+ info->fd = -1;
+ info->process_type_length = process_type_.length();
+ // Freed in CrashDumpTask().
+ char* process_type_str = new char[info->process_type_length + 1];
+ process_type_.copy(process_type_str, info->process_type_length);
+ process_type_str[info->process_type_length] = '\0';
+ info->process_type = process_type_str;
+
+ // Memory released from scoped_ptrs below are also freed in CrashDumpTask().
+ info->crash_keys = crash_keys.release();
+#if defined(ADDRESS_SANITIZER)
+ asan_report[kMaxAsanReportSize] = '\0';
+ info->asan_report_str = asan_report.release();
+ info->asan_report_length = strlen(info->asan_report_str);
+#endif
+
+ info->process_start_time = uptime;
+ info->oom_size = oom_size;
+#if defined(OS_ANDROID)
+ // Nothing gets uploaded in android.
+ info->upload = false;
+#else
+ info->upload = upload_;
+#endif
+
+
+ BrowserThread::GetBlockingPool()->PostSequencedWorkerTask(
+ worker_pool_token_,
+ FROM_HERE,
+ base::Bind(&CrashHandlerHostLinux::WriteDumpFile,
+ base::Unretained(this),
+ base::Passed(&info),
+ base::Passed(&crash_context),
+ crashing_pid,
+ signal_fd.release()));
+}
+
+void CrashHandlerHostLinux::WriteDumpFile(scoped_ptr<BreakpadInfo> info,
+ scoped_ptr<char[]> crash_context,
+ pid_t crashing_pid,
+ int signal_fd) {
+ DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
+ worker_pool_token_));
+
+ // Set |info->distro| here because base::GetLinuxDistro() needs to run on a
+ // blocking thread.
+ std::string distro = base::GetLinuxDistro();
+ info->distro_length = distro.length();
+ // Freed in CrashDumpTask().
+ char* distro_str = new char[info->distro_length + 1];
+ distro.copy(distro_str, info->distro_length);
+ distro_str[info->distro_length] = '\0';
+ info->distro = distro_str;
+
+ base::FilePath dumps_path("/tmp");
+ PathService::Get(base::DIR_TEMP, &dumps_path);
+ if (!info->upload)
+ dumps_path = dumps_path_;
+ const std::string minidump_filename =
+ base::StringPrintf("%s/chromium-%s-minidump-%016" PRIx64 ".dmp",
+ dumps_path.value().c_str(),
+ process_type_.c_str(),
+ base::RandUint64());
+
+ if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
+ kMaxMinidumpFileSize,
+ crashing_pid,
+ crash_context.get(),
+ kCrashContextSize,
+ google_breakpad::MappingList(),
+ google_breakpad::AppMemoryList())) {
+ LOG(ERROR) << "Failed to write crash dump for pid " << crashing_pid;
+ }
+#if defined(ADDRESS_SANITIZER)
+ // Create a temporary file holding the AddressSanitizer report.
+ const base::FilePath log_path =
+ base::FilePath(minidump_filename).ReplaceExtension("log");
+ base::WriteFile(log_path, info->asan_report_str, info->asan_report_length);
+#endif
+
+ // Freed in CrashDumpTask().
+ char* minidump_filename_str = new char[minidump_filename.length() + 1];
+ minidump_filename.copy(minidump_filename_str, minidump_filename.length());
+ minidump_filename_str[minidump_filename.length()] = '\0';
+ info->filename = minidump_filename_str;
+#if defined(ADDRESS_SANITIZER)
+ // Freed in CrashDumpTask().
+ char* minidump_log_filename_str = new char[minidump_filename.length() + 1];
+ minidump_filename.copy(minidump_log_filename_str, minidump_filename.length());
+ memcpy(minidump_log_filename_str + minidump_filename.length() - 3, "log", 3);
+ minidump_log_filename_str[minidump_filename.length()] = '\0';
+ info->log_filename = minidump_log_filename_str;
+#endif
+ info->pid = crashing_pid;
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&CrashHandlerHostLinux::QueueCrashDumpTask,
+ base::Unretained(this),
+ base::Passed(&info),
+ signal_fd));
+}
+
+void CrashHandlerHostLinux::QueueCrashDumpTask(scoped_ptr<BreakpadInfo> info,
+ int signal_fd) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ // Send the done signal to the process: it can exit now.
+ struct msghdr msg = {0};
+ struct iovec done_iov;
+ done_iov.iov_base = const_cast<char*>("\x42");
+ done_iov.iov_len = 1;
+ msg.msg_iov = &done_iov;
+ msg.msg_iovlen = 1;
+
+ HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL));
+ close(signal_fd);
+
+ uploader_thread_->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&CrashDumpTask, base::Unretained(this), base::Passed(&info)));
+}
+
+void CrashHandlerHostLinux::WillDestroyCurrentMessageLoop() {
+ file_descriptor_watcher_.StopWatchingFileDescriptor();
+
+ // If we are quitting and there are crash dumps in the queue, turn them into
+ // no-ops.
+ shutting_down_ = true;
+ uploader_thread_->Stop();
+}
+
+bool CrashHandlerHostLinux::IsShuttingDown() const {
+ return shutting_down_;
+}
+
+} // namespace breakpad
diff --git a/components/crash/content/browser/crash_handler_host_linux.h b/components/crash/content/browser/crash_handler_host_linux.h
new file mode 100644
index 0000000..92ef522
--- /dev/null
+++ b/components/crash/content/browser/crash_handler_host_linux.h
@@ -0,0 +1,93 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_HANDLER_HOST_LINUX_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_HANDLER_HOST_LINUX_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/sequenced_worker_pool.h"
+
+namespace base {
+class Thread;
+}
+
+namespace breakpad {
+
+struct BreakpadInfo;
+
+// This is the host for processes which run breakpad inside the sandbox on
+// Linux or Android. We perform the crash dump from the browser because it
+// allows us to be outside the sandbox.
+//
+// Processes signal that they need to be dumped by sending a datagram over a
+// UNIX domain socket. All processes of the same type share the client end of
+// this socket which is installed in their descriptor table before exec.
+class CrashHandlerHostLinux : public base::MessageLoopForIO::Watcher,
+ public base::MessageLoop::DestructionObserver {
+ public:
+ CrashHandlerHostLinux(const std::string& process_type,
+ const base::FilePath& dumps_path,
+ bool upload);
+ ~CrashHandlerHostLinux() override;
+
+ // Starts the uploader thread. Must be called immediately after creating the
+ // class.
+ void StartUploaderThread();
+
+ // Get the file descriptor which processes should be given in order to signal
+ // crashes to the browser.
+ int GetDeathSignalSocket() const {
+ return process_socket_;
+ }
+
+ // MessagePumbLibevent::Watcher impl:
+ void OnFileCanWriteWithoutBlocking(int fd) override;
+ void OnFileCanReadWithoutBlocking(int fd) override;
+
+ // MessageLoop::DestructionObserver impl:
+ void WillDestroyCurrentMessageLoop() override;
+
+ // Whether we are shutting down or not.
+ bool IsShuttingDown() const;
+
+ private:
+ void Init();
+
+ // Do work in a sequenced worker pool for OnFileCanReadWithoutBlocking().
+ void WriteDumpFile(scoped_ptr<BreakpadInfo> info,
+ scoped_ptr<char[]> crash_context,
+ pid_t crashing_pid,
+ int signal_fd);
+
+ // Continue OnFileCanReadWithoutBlocking()'s work on the IO thread.
+ void QueueCrashDumpTask(scoped_ptr<BreakpadInfo> info, int signal_fd);
+
+ std::string process_type_;
+ base::FilePath dumps_path_;
+ bool upload_;
+
+ int process_socket_;
+ int browser_socket_;
+
+ base::MessageLoopForIO::FileDescriptorWatcher file_descriptor_watcher_;
+ scoped_ptr<base::Thread> uploader_thread_;
+ bool shutting_down_;
+
+ // Unique sequence token so that writing crash dump won't be blocked
+ // by other tasks.
+ base::SequencedWorkerPool::SequenceToken worker_pool_token_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashHandlerHostLinux);
+};
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_HANDLER_HOST_LINUX_H_
diff --git a/components/crash/content/tools/BUILD.gn b/components/crash/content/tools/BUILD.gn
new file mode 100644
index 0000000..82f282b
--- /dev/null
+++ b/components/crash/content/tools/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (is_win) {
+ source_set("crash_service") {
+ sources = [
+ "crash_service.cc",
+ "crash_service.h",
+ ]
+
+ deps = [
+ "//base",
+ "//breakpad:breakpad_handler",
+ "//breakpad:breakpad_sender",
+ ]
+ }
+}
diff --git a/components/crash/content/tools/crash_service.cc b/components/crash/content/tools/crash_service.cc
new file mode 100644
index 0000000..ed55bbf
--- /dev/null
+++ b/components/crash/content/tools/crash_service.cc
@@ -0,0 +1,487 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/tools/crash_service.h"
+
+#include <windows.h>
+
+#include <sddl.h>
+#include <fstream>
+#include <map>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/win/windows_version.h"
+#include "breakpad/src/client/windows/crash_generation/client_info.h"
+#include "breakpad/src/client/windows/crash_generation/crash_generation_server.h"
+#include "breakpad/src/client/windows/sender/crash_report_sender.h"
+
+namespace breakpad {
+
+namespace {
+
+const wchar_t kTestPipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
+
+const wchar_t kCrashReportURL[] = L"https://clients2.google.com/cr/report";
+const wchar_t kCheckPointFile[] = L"crash_checkpoint.txt";
+
+typedef std::map<std::wstring, std::wstring> CrashMap;
+
+bool CustomInfoToMap(const google_breakpad::ClientInfo* client_info,
+ const std::wstring& reporter_tag, CrashMap* map) {
+ google_breakpad::CustomClientInfo info = client_info->GetCustomInfo();
+
+ for (uintptr_t i = 0; i < info.count; ++i) {
+ (*map)[info.entries[i].name] = info.entries[i].value;
+ }
+
+ (*map)[L"rept"] = reporter_tag;
+
+ return !map->empty();
+}
+
+bool WriteCustomInfoToFile(const std::wstring& dump_path, const CrashMap& map) {
+ std::wstring file_path(dump_path);
+ size_t last_dot = file_path.rfind(L'.');
+ if (last_dot == std::wstring::npos)
+ return false;
+ file_path.resize(last_dot);
+ file_path += L".txt";
+
+ std::wofstream file(file_path.c_str(),
+ std::ios_base::out | std::ios_base::app | std::ios::binary);
+ if (!file.is_open())
+ return false;
+
+ CrashMap::const_iterator pos;
+ for (pos = map.begin(); pos != map.end(); ++pos) {
+ std::wstring line = pos->first;
+ line += L':';
+ line += pos->second;
+ line += L'\n';
+ file.write(line.c_str(), static_cast<std::streamsize>(line.length()));
+ }
+ return true;
+}
+
+// The window procedure task is to handle when a) the user logs off.
+// b) the system shuts down or c) when the user closes the window.
+LRESULT __stdcall CrashSvcWndProc(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ switch (message) {
+ case WM_CLOSE:
+ case WM_ENDSESSION:
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ default:
+ return DefWindowProc(hwnd, message, wparam, lparam);
+ }
+ return 0;
+}
+
+// This is the main and only application window.
+HWND g_top_window = NULL;
+
+bool CreateTopWindow(HINSTANCE instance, bool visible) {
+ WNDCLASSEXW wcx = {0};
+ wcx.cbSize = sizeof(wcx);
+ wcx.style = CS_HREDRAW | CS_VREDRAW;
+ wcx.lpfnWndProc = CrashSvcWndProc;
+ wcx.hInstance = instance;
+ wcx.lpszClassName = L"crash_svc_class";
+ ::RegisterClassExW(&wcx);
+ DWORD style = visible ? WS_POPUPWINDOW | WS_VISIBLE : WS_OVERLAPPED;
+
+ // The window size is zero but being a popup window still shows in the
+ // task bar and can be closed using the system menu or using task manager.
+ HWND window = CreateWindowExW(0, wcx.lpszClassName, L"crash service", style,
+ CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
+ NULL, NULL, instance, NULL);
+ if (!window)
+ return false;
+
+ ::UpdateWindow(window);
+ VLOG(1) << "window handle is " << window;
+ g_top_window = window;
+ return true;
+}
+
+// Simple helper class to keep the process alive until the current request
+// finishes.
+class ProcessingLock {
+ public:
+ ProcessingLock() {
+ ::InterlockedIncrement(&op_count_);
+ }
+ ~ProcessingLock() {
+ ::InterlockedDecrement(&op_count_);
+ }
+ static bool IsWorking() {
+ return (op_count_ != 0);
+ }
+ private:
+ static volatile LONG op_count_;
+};
+
+volatile LONG ProcessingLock::op_count_ = 0;
+
+// This structure contains the information that the worker thread needs to
+// send a crash dump to the server.
+struct DumpJobInfo {
+ DWORD pid;
+ CrashService* self;
+ CrashMap map;
+ std::wstring dump_path;
+
+ DumpJobInfo(DWORD process_id, CrashService* service,
+ const CrashMap& crash_map, const std::wstring& path)
+ : pid(process_id), self(service), map(crash_map), dump_path(path) {
+ }
+};
+
+} // namespace
+
+// Command line switches:
+const char CrashService::kMaxReports[] = "max-reports";
+const char CrashService::kNoWindow[] = "no-window";
+const char CrashService::kReporterTag[] = "reporter";
+const char CrashService::kDumpsDir[] = "dumps-dir";
+const char CrashService::kPipeName[] = "pipe-name";
+
+CrashService::CrashService()
+ : dumper_(NULL),
+ sender_(NULL),
+ requests_handled_(0),
+ requests_sent_(0),
+ clients_connected_(0),
+ clients_terminated_(0) {
+}
+
+CrashService::~CrashService() {
+ base::AutoLock lock(sending_);
+ delete dumper_;
+ delete sender_;
+}
+
+bool CrashService::Initialize(const base::FilePath& operating_dir,
+ const base::FilePath& dumps_path) {
+ using google_breakpad::CrashReportSender;
+ using google_breakpad::CrashGenerationServer;
+
+ std::wstring pipe_name = kTestPipeName;
+ int max_reports = -1;
+
+ // The checkpoint file allows CrashReportSender to enforce the the maximum
+ // reports per day quota. Does not seem to serve any other purpose.
+ base::FilePath checkpoint_path = operating_dir.Append(kCheckPointFile);
+
+ base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
+
+ base::FilePath dumps_path_to_use = dumps_path;
+
+ if (cmd_line.HasSwitch(kDumpsDir)) {
+ dumps_path_to_use =
+ base::FilePath(cmd_line.GetSwitchValueNative(kDumpsDir));
+ }
+
+ // We can override the send reports quota with a command line switch.
+ if (cmd_line.HasSwitch(kMaxReports))
+ max_reports = _wtoi(cmd_line.GetSwitchValueNative(kMaxReports).c_str());
+
+ // Allow the global pipe name to be overridden for better testability.
+ if (cmd_line.HasSwitch(kPipeName))
+ pipe_name = cmd_line.GetSwitchValueNative(kPipeName);
+
+#ifdef _WIN64
+ pipe_name += L"-x64";
+#endif
+
+ if (max_reports > 0) {
+ // Create the http sender object.
+ sender_ = new CrashReportSender(checkpoint_path.value());
+ sender_->set_max_reports_per_day(max_reports);
+ }
+
+ SECURITY_ATTRIBUTES security_attributes = {0};
+ SECURITY_ATTRIBUTES* security_attributes_actual = NULL;
+
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
+ SECURITY_DESCRIPTOR* security_descriptor =
+ reinterpret_cast<SECURITY_DESCRIPTOR*>(
+ GetSecurityDescriptorForLowIntegrity());
+ DCHECK(security_descriptor != NULL);
+
+ security_attributes.nLength = sizeof(security_attributes);
+ security_attributes.lpSecurityDescriptor = security_descriptor;
+ security_attributes.bInheritHandle = FALSE;
+
+ security_attributes_actual = &security_attributes;
+ }
+
+ // Create the OOP crash generator object.
+ dumper_ = new CrashGenerationServer(pipe_name, security_attributes_actual,
+ &CrashService::OnClientConnected, this,
+ &CrashService::OnClientDumpRequest, this,
+ &CrashService::OnClientExited, this,
+ NULL, NULL,
+ true, &dumps_path_to_use.value());
+
+ if (!dumper_) {
+ LOG(ERROR) << "could not create dumper";
+ if (security_attributes.lpSecurityDescriptor)
+ LocalFree(security_attributes.lpSecurityDescriptor);
+ return false;
+ }
+
+ if (!CreateTopWindow(::GetModuleHandleW(NULL),
+ !cmd_line.HasSwitch(kNoWindow))) {
+ LOG(ERROR) << "could not create window";
+ if (security_attributes.lpSecurityDescriptor)
+ LocalFree(security_attributes.lpSecurityDescriptor);
+ return false;
+ }
+
+ reporter_tag_ = L"crash svc";
+ if (cmd_line.HasSwitch(kReporterTag))
+ reporter_tag_ = cmd_line.GetSwitchValueNative(kReporterTag);
+
+ // Log basic information.
+ VLOG(1) << "pipe name is " << pipe_name
+ << "\ndumps at " << dumps_path_to_use.value();
+
+ if (sender_) {
+ VLOG(1) << "checkpoint is " << checkpoint_path.value()
+ << "\nserver is " << kCrashReportURL
+ << "\nmaximum " << sender_->max_reports_per_day() << " reports/day"
+ << "\nreporter is " << reporter_tag_;
+ }
+ // Start servicing clients.
+ if (!dumper_->Start()) {
+ LOG(ERROR) << "could not start dumper";
+ if (security_attributes.lpSecurityDescriptor)
+ LocalFree(security_attributes.lpSecurityDescriptor);
+ return false;
+ }
+
+ if (security_attributes.lpSecurityDescriptor)
+ LocalFree(security_attributes.lpSecurityDescriptor);
+
+ // This is throwaway code. We don't need to sync with the browser process
+ // once Google Update is updated to a version supporting OOP crash handling.
+ // Create or open an event to signal the browser process that the crash
+ // service is initialized.
+ HANDLE running_event =
+ ::CreateEventW(NULL, TRUE, TRUE, L"g_chrome_crash_svc");
+ // If the browser already had the event open, the CreateEvent call did not
+ // signal it. We need to do it manually.
+ ::SetEvent(running_event);
+
+ return true;
+}
+
+void CrashService::OnClientConnected(void* context,
+ const google_breakpad::ClientInfo* client_info) {
+ ProcessingLock lock;
+ VLOG(1) << "client start. pid = " << client_info->pid();
+ CrashService* self = static_cast<CrashService*>(context);
+ ::InterlockedIncrement(&self->clients_connected_);
+}
+
+void CrashService::OnClientExited(void* context,
+ const google_breakpad::ClientInfo* client_info) {
+ ProcessingLock lock;
+ VLOG(1) << "client end. pid = " << client_info->pid();
+ CrashService* self = static_cast<CrashService*>(context);
+ ::InterlockedIncrement(&self->clients_terminated_);
+
+ if (!self->sender_)
+ return;
+
+ // When we are instructed to send reports we need to exit if there are
+ // no more clients to service. The next client that runs will start us.
+ // Only chrome.exe starts crash_service with a non-zero max_reports.
+ if (self->clients_connected_ > self->clients_terminated_)
+ return;
+ if (self->sender_->max_reports_per_day() > 0) {
+ // Wait for the other thread to send crashes, if applicable. The sender
+ // thread takes the sending_ lock, so the sleep is just to give it a
+ // chance to start.
+ ::Sleep(1000);
+ base::AutoLock lock(self->sending_);
+ // Some people can restart chrome very fast, check again if we have
+ // a new client before exiting for real.
+ if (self->clients_connected_ == self->clients_terminated_) {
+ VLOG(1) << "zero clients. exiting";
+ ::PostMessage(g_top_window, WM_CLOSE, 0, 0);
+ }
+ }
+}
+
+void CrashService::OnClientDumpRequest(void* context,
+ const google_breakpad::ClientInfo* client_info,
+ const std::wstring* file_path) {
+ ProcessingLock lock;
+
+ if (!file_path) {
+ LOG(ERROR) << "dump with no file path";
+ return;
+ }
+ if (!client_info) {
+ LOG(ERROR) << "dump with no client info";
+ return;
+ }
+
+ CrashService* self = static_cast<CrashService*>(context);
+ if (!self) {
+ LOG(ERROR) << "dump with no context";
+ return;
+ }
+
+ CrashMap map;
+ CustomInfoToMap(client_info, self->reporter_tag_, &map);
+
+ // Move dump file to the directory under client breakpad dump location.
+ base::FilePath dump_location = base::FilePath(*file_path);
+ CrashMap::const_iterator it = map.find(L"breakpad-dump-location");
+ if (it != map.end()) {
+ base::FilePath alternate_dump_location = base::FilePath(it->second);
+ base::CreateDirectoryW(alternate_dump_location);
+ alternate_dump_location = alternate_dump_location.Append(
+ dump_location.BaseName());
+ base::Move(dump_location, alternate_dump_location);
+ dump_location = alternate_dump_location;
+ }
+
+ DWORD pid = client_info->pid();
+ VLOG(1) << "dump for pid = " << pid << " is " << dump_location.value();
+
+ if (!WriteCustomInfoToFile(dump_location.value(), map)) {
+ LOG(ERROR) << "could not write custom info file";
+ }
+
+ if (!self->sender_)
+ return;
+
+ // Send the crash dump using a worker thread. This operation has retry
+ // logic in case there is no internet connection at the time.
+ DumpJobInfo* dump_job = new DumpJobInfo(pid, self, map,
+ dump_location.value());
+ if (!::QueueUserWorkItem(&CrashService::AsyncSendDump,
+ dump_job, WT_EXECUTELONGFUNCTION)) {
+ LOG(ERROR) << "could not queue job";
+ }
+}
+
+// We are going to try sending the report several times. If we can't send,
+// we sleep from one minute to several hours depending on the retry round.
+unsigned long CrashService::AsyncSendDump(void* context) {
+ if (!context)
+ return 0;
+
+ DumpJobInfo* info = static_cast<DumpJobInfo*>(context);
+
+ std::wstring report_id = L"<unsent>";
+
+ const DWORD kOneMinute = 60*1000;
+ const DWORD kOneHour = 60*kOneMinute;
+
+ const DWORD kSleepSchedule[] = {
+ 24*kOneHour,
+ 8*kOneHour,
+ 4*kOneHour,
+ kOneHour,
+ 15*kOneMinute,
+ 0};
+
+ int retry_round = arraysize(kSleepSchedule) - 1;
+
+ do {
+ ::Sleep(kSleepSchedule[retry_round]);
+ {
+ // Take the server lock while sending. This also prevent early
+ // termination of the service object.
+ base::AutoLock lock(info->self->sending_);
+ VLOG(1) << "trying to send report for pid = " << info->pid;
+ google_breakpad::ReportResult send_result
+ = info->self->sender_->SendCrashReport(kCrashReportURL, info->map,
+ info->dump_path, &report_id);
+ switch (send_result) {
+ case google_breakpad::RESULT_FAILED:
+ report_id = L"<network issue>";
+ break;
+ case google_breakpad::RESULT_REJECTED:
+ report_id = L"<rejected>";
+ ++info->self->requests_handled_;
+ retry_round = 0;
+ break;
+ case google_breakpad::RESULT_SUCCEEDED:
+ ++info->self->requests_sent_;
+ ++info->self->requests_handled_;
+ retry_round = 0;
+ break;
+ case google_breakpad::RESULT_THROTTLED:
+ report_id = L"<throttled>";
+ break;
+ default:
+ report_id = L"<unknown>";
+ break;
+ };
+ }
+
+ VLOG(1) << "dump for pid =" << info->pid << " crash2 id =" << report_id;
+ --retry_round;
+ } while (retry_round >= 0);
+
+ if (!::DeleteFileW(info->dump_path.c_str()))
+ LOG(WARNING) << "could not delete " << info->dump_path;
+
+ delete info;
+ return 0;
+}
+
+int CrashService::ProcessingLoop() {
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ VLOG(1) << "session ending..";
+ while (ProcessingLock::IsWorking()) {
+ ::Sleep(50);
+ }
+
+ VLOG(1) << "clients connected :" << clients_connected_
+ << "\nclients terminated :" << clients_terminated_
+ << "\ndumps serviced :" << requests_handled_
+ << "\ndumps reported :" << requests_sent_;
+
+ return static_cast<int>(msg.wParam);
+}
+
+PSECURITY_DESCRIPTOR CrashService::GetSecurityDescriptorForLowIntegrity() {
+ // Build the SDDL string for the label.
+ std::wstring sddl = L"S:(ML;;NW;;;S-1-16-4096)";
+
+ PSECURITY_DESCRIPTOR sec_desc = NULL;
+
+ PACL sacl = NULL;
+ BOOL sacl_present = FALSE;
+ BOOL sacl_defaulted = FALSE;
+
+ if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(),
+ SDDL_REVISION,
+ &sec_desc, NULL)) {
+ if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
+ &sacl_defaulted)) {
+ return sec_desc;
+ }
+ }
+
+ return NULL;
+}
+
+} // namespace breakpad
diff --git a/components/crash/content/tools/crash_service.h b/components/crash/content/tools/crash_service.h
new file mode 100644
index 0000000..39335e3
--- /dev/null
+++ b/components/crash/content/tools/crash_service.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRASH_CONTENT_TOOLS_CRASH_SERVICE_H_
+#define COMPONENTS_CRASH_CONTENT_TOOLS_CRASH_SERVICE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/synchronization/lock.h"
+
+namespace google_breakpad {
+
+class CrashReportSender;
+class CrashGenerationServer;
+class ClientInfo;
+
+}
+
+namespace breakpad {
+
+// This class implements an out-of-process crash server. It uses breakpad's
+// CrashGenerationServer and CrashReportSender to generate and then send the
+// crash dumps. Internally, it uses OS specific pipe to allow applications to
+// register for crash dumps and later on when a registered application crashes
+// it will signal an event that causes this code to wake up and perform a
+// crash dump on the signaling process. The dump is then stored on disk and
+// possibly sent to the crash2 servers.
+class CrashService {
+ public:
+ CrashService();
+ ~CrashService();
+
+ // Starts servicing crash dumps. Returns false if it failed. Do not use
+ // other members in that case. |operating_dir| is where the CrashService
+ // should store breakpad's checkpoint file. |dumps_path| is the directory
+ // where the crash dumps should be stored.
+ bool Initialize(const base::FilePath& operating_dir,
+ const base::FilePath& dumps_path);
+
+ // Command line switches:
+ //
+ // --max-reports=<number>
+ // Allows to override the maximum number for reports per day. Normally
+ // the crash dumps are never sent so if you want to send any you must
+ // specify a positive number here.
+ static const char kMaxReports[];
+ // --no-window
+ // Does not create a visible window on the desktop. The window does not have
+ // any other functionality other than allowing the crash service to be
+ // gracefully closed.
+ static const char kNoWindow[];
+ // --reporter=<string>
+ // Allows to specify a custom string that appears on the detail crash report
+ // page in the crash server. This should be a 25 chars or less string.
+ // The default tag if not specified is 'crash svc'.
+ static const char kReporterTag[];
+ // --dumps-dir=<directory-path>
+ // Override the directory to which crash dump files will be written.
+ static const char kDumpsDir[];
+ // --pipe-name=<string>
+ // Override the name of the Windows named pipe on which we will
+ // listen for crash dump request messages.
+ static const char kPipeName[];
+
+ // Returns number of crash dumps handled.
+ int requests_handled() const {
+ return requests_handled_;
+ }
+ // Returns number of crash clients registered.
+ int clients_connected() const {
+ return clients_connected_;
+ }
+ // Returns number of crash clients terminated.
+ int clients_terminated() const {
+ return clients_terminated_;
+ }
+
+ // Starts the processing loop. This function does not return unless the
+ // user is logging off or the user closes the crash service window. The
+ // return value is a good number to pass in ExitProcess().
+ int ProcessingLoop();
+
+ private:
+ static void OnClientConnected(void* context,
+ const google_breakpad::ClientInfo* client_info);
+
+ static void OnClientDumpRequest(
+ void* context,
+ const google_breakpad::ClientInfo* client_info,
+ const std::wstring* file_path);
+
+ static void OnClientExited(void* context,
+ const google_breakpad::ClientInfo* client_info);
+
+ // This routine sends the crash dump to the server. It takes the sending_
+ // lock when it is performing the send.
+ static unsigned long __stdcall AsyncSendDump(void* context);
+
+ // Returns the security descriptor which access to low integrity processes
+ // The caller is supposed to free the security descriptor by calling
+ // LocalFree.
+ PSECURITY_DESCRIPTOR GetSecurityDescriptorForLowIntegrity();
+
+ google_breakpad::CrashGenerationServer* dumper_;
+ google_breakpad::CrashReportSender* sender_;
+
+ // the extra tag sent to the server with each dump.
+ std::wstring reporter_tag_;
+
+ // clients serviced statistics:
+ int requests_handled_;
+ int requests_sent_;
+ volatile long clients_connected_;
+ volatile long clients_terminated_;
+ base::Lock sending_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashService);
+};
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_TOOLS_CRASH_SERVICE_H_
diff --git a/components/crash/content/tools/dmp2minidump.py b/components/crash/content/tools/dmp2minidump.py
new file mode 100755
index 0000000..7823d48
--- /dev/null
+++ b/components/crash/content/tools/dmp2minidump.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A tool to extract minidumps from dmp crash dumps."""
+
+import os
+import sys
+from cgi import parse_multipart
+
+
+def ProcessDump(dump_file, minidump_file):
+ """Extracts the part of the dump file that minidump_stackwalk can read.
+
+ The dump files generated by the breakpad integration multi-part form data
+ that include the minidump as file attachment.
+
+ Args:
+ dump_file: the dump file that needs to be processed.
+ minidump_file: the file to write the minidump to.
+ """
+ try:
+ dump = open(dump_file, 'rb')
+ boundary = dump.readline().strip()[2:]
+ data = parse_multipart(dump, {'boundary': boundary})
+ except:
+ print 'Failed to read dmp file %s' % dump_file
+ return
+
+ if not 'upload_file_minidump' in data:
+ print 'Could not find minidump file in dump.'
+ return
+
+ f = open(minidump_file, 'w')
+ f.write("\r\n".join(data['upload_file_minidump']))
+ f.close()
+
+
+def main():
+ if len(sys.argv) != 3:
+ print 'Usage: %s [dmp file] [minidump]' % sys.argv[0]
+ print ''
+ print 'Extracts the minidump stored in the crash dump file'
+ return 1
+
+ ProcessDump(sys.argv[1], sys.argv[2])
+
+
+if '__main__' == __name__:
+ sys.exit(main())
diff --git a/components/crash/content/tools/generate_breakpad_symbols.py b/components/crash/content/tools/generate_breakpad_symbols.py
new file mode 100755
index 0000000..5f8fb85
--- /dev/null
+++ b/components/crash/content/tools/generate_breakpad_symbols.py
@@ -0,0 +1,262 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A tool to generate symbols for a binary suitable for breakpad.
+
+Currently, the tool only supports Linux, Android, and Mac. Support for other
+platforms is planned.
+"""
+
+import errno
+import optparse
+import os
+import Queue
+import re
+import shutil
+import subprocess
+import sys
+import threading
+
+
+CONCURRENT_TASKS=4
+
+
+def GetCommandOutput(command):
+ """Runs the command list, returning its output.
+
+ Prints the given command (which should be a list of one or more strings),
+ then runs it and returns its output (stdout) as a string.
+
+ From chromium_utils.
+ """
+ devnull = open(os.devnull, 'w')
+ proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull,
+ bufsize=1)
+ output = proc.communicate()[0]
+ return output
+
+
+def GetDumpSymsBinary(build_dir=None):
+ """Returns the path to the dump_syms binary."""
+ DUMP_SYMS = 'dump_syms'
+ dump_syms_bin = os.path.join(os.path.expanduser(build_dir), DUMP_SYMS)
+ if not os.access(dump_syms_bin, os.X_OK):
+ print 'Cannot find %s.' % dump_syms_bin
+ return None
+
+ return dump_syms_bin
+
+
+def Resolve(path, exe_path, loader_path, rpaths):
+ """Resolve a dyld path.
+
+ @executable_path is replaced with |exe_path|
+ @loader_path is replaced with |loader_path|
+ @rpath is replaced with the first path in |rpaths| where the referenced file
+ is found
+ """
+ path = path.replace('@loader_path', loader_path)
+ path = path.replace('@executable_path', exe_path)
+ if path.find('@rpath') != -1:
+ for rpath in rpaths:
+ new_path = Resolve(path.replace('@rpath', rpath), exe_path, loader_path,
+ [])
+ if os.access(new_path, os.X_OK):
+ return new_path
+ return ''
+ return path
+
+
+def GetSharedLibraryDependenciesLinux(binary):
+ """Return absolute paths to all shared library dependecies of the binary.
+
+ This implementation assumes that we're running on a Linux system."""
+ ldd = GetCommandOutput(['ldd', binary])
+ lib_re = re.compile('\t.* => (.+) \(.*\)$')
+ result = []
+ for line in ldd.splitlines():
+ m = lib_re.match(line)
+ if m:
+ result.append(m.group(1))
+ return result
+
+
+def GetSharedLibraryDependenciesMac(binary, exe_path):
+ """Return absolute paths to all shared library dependecies of the binary.
+
+ This implementation assumes that we're running on a Mac system."""
+ loader_path = os.path.dirname(binary)
+ otool = GetCommandOutput(['otool', '-l', binary]).splitlines()
+ rpaths = []
+ for idx, line in enumerate(otool):
+ if line.find('cmd LC_RPATH') != -1:
+ m = re.match(' *path (.*) \(offset .*\)$', otool[idx+2])
+ rpaths.append(m.group(1))
+
+ otool = GetCommandOutput(['otool', '-L', binary]).splitlines()
+ lib_re = re.compile('\t(.*) \(compatibility .*\)$')
+ deps = []
+ for line in otool:
+ m = lib_re.match(line)
+ if m:
+ dep = Resolve(m.group(1), exe_path, loader_path, rpaths)
+ if dep:
+ deps.append(os.path.normpath(dep))
+ return deps
+
+
+def GetSharedLibraryDependencies(options, binary, exe_path):
+ """Return absolute paths to all shared library dependecies of the binary."""
+ deps = []
+ if sys.platform.startswith('linux'):
+ deps = GetSharedLibraryDependenciesLinux(binary)
+ elif sys.platform == 'darwin':
+ deps = GetSharedLibraryDependenciesMac(binary, exe_path)
+ else:
+ print "Platform not supported."
+ sys.exit(1)
+
+ result = []
+ build_dir = os.path.abspath(options.build_dir)
+ for dep in deps:
+ if (os.access(dep, os.X_OK) and
+ os.path.abspath(os.path.dirname(dep)).startswith(build_dir)):
+ result.append(dep)
+ return result
+
+
+def mkdir_p(path):
+ """Simulates mkdir -p."""
+ try:
+ os.makedirs(path)
+ except OSError as e:
+ if e.errno == errno.EEXIST and os.path.isdir(path):
+ pass
+ else: raise
+
+
+def GenerateSymbols(options, binaries):
+ """Dumps the symbols of binary and places them in the given directory."""
+
+ queue = Queue.Queue()
+ print_lock = threading.Lock()
+
+ def _Worker():
+ while True:
+ binary = queue.get()
+
+ should_dump_syms = True
+ reason = "no reason"
+
+ output_path = os.path.join(
+ options.symbols_dir, os.path.basename(binary))
+ if os.path.isdir(output_path):
+ if os.path.getmtime(binary) < os.path.getmtime(output_path):
+ should_dump_syms = False
+ reason = "symbols are more current than binary"
+
+ if not should_dump_syms:
+ if options.verbose:
+ with print_lock:
+ print "Skipping %s (%s)" % (binary, reason)
+ queue.task_done()
+ continue
+
+ if options.verbose:
+ with print_lock:
+ print "Generating symbols for %s" % binary
+
+ if os.path.isdir(output_path):
+ os.utime(output_path, None)
+
+ syms = GetCommandOutput([GetDumpSymsBinary(options.build_dir), '-r',
+ binary])
+ module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n", syms)
+ output_path = os.path.join(options.symbols_dir, module_line.group(2),
+ module_line.group(1))
+ mkdir_p(output_path)
+ symbol_file = "%s.sym" % module_line.group(2)
+ try:
+ f = open(os.path.join(output_path, symbol_file), 'w')
+ f.write(syms)
+ f.close()
+ except Exception, e:
+ # Not much we can do about this.
+ with print_lock:
+ print e
+
+ queue.task_done()
+
+ for binary in binaries:
+ queue.put(binary)
+
+ for _ in range(options.jobs):
+ t = threading.Thread(target=_Worker)
+ t.daemon = True
+ t.start()
+
+ queue.join()
+
+
+def main():
+ parser = optparse.OptionParser()
+ parser.add_option('', '--build-dir', default='',
+ help='The build output directory.')
+ parser.add_option('', '--symbols-dir', default='',
+ help='The directory where to write the symbols file.')
+ parser.add_option('', '--binary', default='',
+ help='The path of the binary to generate symbols for.')
+ parser.add_option('', '--clear', default=False, action='store_true',
+ help='Clear the symbols directory before writing new '
+ 'symbols.')
+ parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store',
+ type='int', help='Number of parallel tasks to run.')
+ parser.add_option('-v', '--verbose', action='store_true',
+ help='Print verbose status output.')
+
+ (options, _) = parser.parse_args()
+
+ if not options.symbols_dir:
+ print "Required option --symbols-dir missing."
+ return 1
+
+ if not options.build_dir:
+ print "Required option --build-dir missing."
+ return 1
+
+ if not options.binary:
+ print "Required option --binary missing."
+ return 1
+
+ if not os.access(options.binary, os.X_OK):
+ print "Cannot find %s." % options.binary
+ return 1
+
+ if options.clear:
+ try:
+ shutil.rmtree(options.symbols_dir)
+ except:
+ pass
+
+ if not GetDumpSymsBinary(options.build_dir):
+ return 1
+
+ # Build the transitive closure of all dependencies.
+ binaries = set([options.binary])
+ queue = [options.binary]
+ exe_path = os.path.dirname(options.binary)
+ while queue:
+ deps = GetSharedLibraryDependencies(options, queue.pop(0), exe_path)
+ new_deps = set(deps) - binaries
+ binaries |= new_deps
+ queue.extend(list(new_deps))
+
+ GenerateSymbols(options, binaries)
+
+ return 0
+
+
+if '__main__' == __name__:
+ sys.exit(main())