summaryrefslogtreecommitdiffstats
path: root/components/crash
diff options
context:
space:
mode:
authorscottmg <scottmg@chromium.org>2015-11-25 13:55:47 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-25 21:57:07 +0000
commitaaa2ff656f9fcabcbdd9e9964e3b2bdc8f5102ed (patch)
tree28f1fef91043cd3f7a30c85ce59e56b057102207 /components/crash
parentcbc1cd295bc85219ad89326457cd7f5cf003d41c (diff)
downloadchromium_src-aaa2ff656f9fcabcbdd9e9964e3b2bdc8f5102ed.zip
chromium_src-aaa2ff656f9fcabcbdd9e9964e3b2bdc8f5102ed.tar.gz
chromium_src-aaa2ff656f9fcabcbdd9e9964e3b2bdc8f5102ed.tar.bz2
Crashpad Windows: Use the Crashpad client instead of Breakpad on Windows
Crashpad is always compiled into chrome and its handler is always enabled. It only uploads in Official builds. On Windows, the crash handler is chrome.exe run with a --crashpad-handler argument. This is due to concern about incompatibilities of shipping an additional new different binary for AV, firewalls, etc. Sample renderer crash/1aed2bc785e28995 Sample browser: crash/66c822815474a5d8 See also http://crbug.com/427611 . R=mark@chromium.org,cpu@chromium.org BUG=447073,546288, 456193 Review URL: https://codereview.chromium.org/1416133003 Cr-Commit-Position: refs/heads/master@{#361742}
Diffstat (limited to 'components/crash')
-rw-r--r--components/crash/content/app/BUILD.gn40
-rw-r--r--components/crash/content/app/crashpad.cc236
-rw-r--r--components/crash/content/app/crashpad.h (renamed from components/crash/content/app/crashpad_mac.h)33
-rw-r--r--components/crash/content/app/crashpad_mac.mm195
-rw-r--r--components/crash/content/app/crashpad_win.cc202
-rw-r--r--components/crash/content/browser/BUILD.gn2
-rw-r--r--components/crash/core/common/crash_keys.cc12
-rw-r--r--components/crash/core/common/crash_keys.h9
8 files changed, 510 insertions, 219 deletions
diff --git a/components/crash/content/app/BUILD.gn b/components/crash/content/app/BUILD.gn
index cc610d6..5bd0494 100644
--- a/components/crash/content/app/BUILD.gn
+++ b/components/crash/content/app/BUILD.gn
@@ -28,24 +28,27 @@ source_set("lib") {
# GYP version: components/crash.gypi:crash_component
source_set("app") {
sources = [
- "crashpad_mac.h",
+ "crashpad.h",
"crashpad_mac.mm",
+ "crashpad_win.cc",
]
+ if (is_mac || is_win) {
+ sources += [ "crashpad.cc" ]
+ }
+
defines = [ "CRASH_IMPLEMENTATION" ]
public_deps = [
- ":app_non_mac",
+ ":app_non_mac_win",
]
deps = [
"//base",
]
- if (is_mac) {
- deps += [
- ":lib",
- "//third_party/crashpad/crashpad/client",
- ]
+ deps += [ ":lib" ]
+ if (is_mac || is_win) {
+ deps += [ "//third_party/crashpad/crashpad/client" ]
}
}
@@ -71,7 +74,7 @@ source_set("app") {
# crash_component_breakpad_mac_to_be_deleted for old Breakpad behavior on
# all platforms, or preferably, depend on crash_component to get Breakpad
# everywhere except for Mac, where you will get Crashpad.
-source_set("app_non_mac") {
+source_set("app_non_mac_win") {
visibility = [
":*",
"//components/crash/content/browser",
@@ -79,8 +82,6 @@ source_set("app_non_mac") {
sources = [
"breakpad_linux_impl.h",
- "breakpad_win.cc",
- "breakpad_win.h",
"hard_error_handler_win.cc",
"hard_error_handler_win.h",
]
@@ -132,15 +133,17 @@ source_set("app_non_mac") {
# removed shortly and all consumers will be expected to use Crashpad as
# the Mac crash-reporting client. See the comment in the
# crash_component_non_mac target for more details.
-source_set("app_breakpad_mac_to_be_deleted") {
+source_set("app_breakpad_mac_win_to_be_deleted") {
deps = [
- ":app_non_mac",
+ ":app_non_mac_win",
]
- if (is_mac) {
+ if (is_mac || is_win) {
sources = [
"breakpad_mac.h",
"breakpad_mac.mm",
+ "breakpad_win.cc",
+ "breakpad_win.h",
]
defines = [ "CRASH_IMPLEMENTATION" ]
@@ -149,9 +152,18 @@ source_set("app_breakpad_mac_to_be_deleted") {
":lib",
"//base",
"//base:base_static",
- "//breakpad",
"//breakpad:client",
"//components/crash/core/common",
+ "//content/public/common:result_codes",
+ "//sandbox",
]
+
+ if (is_mac) {
+ deps += [ "//breakpad" ]
+ }
+
+ if (is_win) {
+ deps += [ "//breakpad:breakpad_handler" ]
+ }
}
}
diff --git a/components/crash/content/app/crashpad.cc b/components/crash/content/app/crashpad.cc
new file mode 100644
index 0000000..e3b4124
--- /dev/null
+++ b/components/crash/content/app/crashpad.cc
@@ -0,0 +1,236 @@
+// 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.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/command_line.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/logging.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 "build/build_config.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"
+
+#if defined(OS_POSIX)
+#include <unistd.h>
+#endif // OS_POSIX
+
+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;
+ }
+ }
+
+ CHECK_LE(message_start, string.size());
+ 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(bool initial_client, 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();
+
+ if (initial_client) {
+#if defined(OS_MACOSX)
+ // "relauncher" is hard-coded because it's a Chrome --type, but this
+ // component can't see Chrome's switches. This is only used for argument
+ // sanitization.
+ DCHECK(browser_process || process_type == "relauncher");
+#else
+ DCHECK(browser_process);
+#endif // OS_MACOSX
+ } else {
+ DCHECK(!browser_process);
+ }
+
+ // database_path is only valid in the browser process.
+ base::FilePath database_path =
+ internal::PlatformCrashpadInitialization(initial_client, browser_process);
+
+ crashpad::CrashpadInfo* crashpad_info =
+ crashpad::CrashpadInfo::GetCrashpadInfo();
+
+#if defined(OS_MACOSX)
+#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);
+ }
+#endif // OS_MACOSX
+
+ 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));
+#if defined(OS_POSIX)
+ SetCrashKeyValue("pid", base::IntToString(getpid()));
+#elif defined(OS_WIN)
+ SetCrashKeyValue("pid", base::IntToString(::GetCurrentProcessId()));
+#endif
+
+ 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)) {
+ // 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, uploading is also disabled on bots
+ // even if consent is present.
+ enable_uploads = crash_reporter_client->GetCollectStatsConsent() &&
+ !crash_reporter_client->IsRunningUnattended();
+ }
+
+ 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);
+ }
+ }
+
+ std::sort(uploaded_reports->begin(), uploaded_reports->end(),
+ [](const UploadedReport& a, const UploadedReport& b) {
+ return a.creation_time >= b.creation_time;
+ });
+}
+
+} // namespace crash_reporter
diff --git a/components/crash/content/app/crashpad_mac.h b/components/crash/content/app/crashpad.h
index 3e84ca6..6121efb 100644
--- a/components/crash/content/app/crashpad_mac.h
+++ b/components/crash/content/app/crashpad.h
@@ -2,14 +2,16 @@
// 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_
+#ifndef COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_H_
+#define COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_H_
#include <time.h>
#include <string>
#include <vector>
+#include "base/files/file_path.h"
+
namespace crash_reporter {
// Initializes Crashpad in a way that is appropriate for initial_client and
@@ -28,14 +30,14 @@ namespace crash_reporter {
// release mode only). Note that when process_type is empty, initial_client must
// be true.
//
-// process_type may be non-empty with initial_client set to true. This indicates
-// that an exception handler has been inherited but should be discarded in favor
-// of a new Crashpad handler. This configuration should be used infrequently. It
-// is provided to allow an install-from-.dmg relauncher process to disassociate
-// from an old Crashpad handler so that after performing an installation from a
-// disk image, the relauncher process may unmount the disk image that contains
-// its inherited crashpad_handler. This is only supported when initial_client is
-// true and process_type is "relauncher".
+// On Mac, process_type may be non-empty with initial_client set to true. This
+// indicates that an exception handler has been inherited but should be
+// discarded in favor of a new Crashpad handler. This configuration should be
+// used infrequently. It is provided to allow an install-from-.dmg relauncher
+// process to disassociate from an old Crashpad handler so that after performing
+// an installation from a disk image, the relauncher process may unmount the
+// disk image that contains its inherited crashpad_handler. This is only
+// supported when initial_client is true and process_type is "relauncher".
void InitializeCrashpad(bool initial_client, const std::string& process_type);
// Enables or disables crash report upload. This is a property of the Crashpad
@@ -68,6 +70,15 @@ struct UploadedReport {
// disabled.
void GetUploadedReports(std::vector<UploadedReport>* uploaded_reports);
+namespace internal {
+
+// The platform-specific portion of InitializeCrashpad().
+// Returns the database path, if initializing in the browser process.
+base::FilePath PlatformCrashpadInitialization(bool initial_client,
+ bool browser_process);
+
+} // namespace internal
+
} // namespace crash_reporter
-#endif // COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_MAC_H_
+#endif // COMPONENTS_CRASH_CONTENT_APP_CRASHPAD_H_
diff --git a/components/crash/content/app/crashpad_mac.mm b/components/crash/content/app/crashpad_mac.mm
index f87fc96e..5ed4c4e 100644
--- a/components/crash/content/app/crashpad_mac.mm
+++ b/components/crash/content/app/crashpad_mac.mm
@@ -2,7 +2,7 @@
// 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 "components/crash/content/app/crashpad.h"
#include <string.h>
#include <unistd.h>
@@ -11,9 +11,6 @@
#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"
@@ -31,81 +28,10 @@
#include "third_party/crashpad/crashpad/client/simulate_crash.h"
namespace crash_reporter {
+namespace internal {
-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(bool initial_client, 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();
-
- if (initial_client) {
- // "relauncher" is hard-coded because it's a Chrome --type, but this
- // component can't see Chrome's switches. This is only used for argument
- // sanitization.
- DCHECK(browser_process || process_type == "relauncher");
- } else {
- DCHECK(!browser_process);
- }
-
+base::FilePath PlatformCrashpadInitialization(bool initial_client,
+ bool browser_process) {
base::FilePath database_path; // Only valid in the browser process.
if (initial_client) {
@@ -115,6 +41,7 @@ void InitializeCrashpad(bool initial_client, const std::string& process_type) {
framework_bundle_path.Append("Helpers").Append("crashpad_handler");
// Is there a way to recover if this fails?
+ CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
crash_reporter_client->GetCrashDumpLocation(&database_path);
// TODO(mark): Reading the Breakpad keys is temporary and transitional. At
@@ -168,116 +95,8 @@ void InitializeCrashpad(bool initial_client, const std::string& process_type) {
} // @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)) {
- // 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, uploading is also disabled on bots
- // even if consent is present.
- enable_uploads = crash_reporter_client->GetCollectStatsConsent() &&
- !crash_reporter_client->IsRunningUnattended();
- }
-
- 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);
+ return database_path;
}
+} // namespace internal
} // namespace crash_reporter
diff --git a/components/crash/content/app/crashpad_win.cc b/components/crash/content/app/crashpad_win.cc
new file mode 100644
index 0000000..684366e
--- /dev/null
+++ b/components/crash/content/app/crashpad_win.cc
@@ -0,0 +1,202 @@
+// 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.h"
+
+#include "base/environment.h"
+#include "base/lazy_instance.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/path_service.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "components/crash/content/app/crash_reporter_client.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#include "third_party/crashpad/crashpad/client/crashpad_info.h"
+
+namespace crash_reporter {
+namespace internal {
+
+namespace {
+
+base::LazyInstance<crashpad::CrashpadClient>::Leaky g_crashpad_client =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+base::FilePath PlatformCrashpadInitialization(bool initial_client,
+ bool browser_process) {
+ base::FilePath database_path; // Only valid in the browser process.
+ bool result;
+
+ const char kPipeNameVar[] = "CHROME_CRASHPAD_PIPE_NAME";
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+
+ DCHECK_EQ(initial_client, browser_process);
+
+ if (initial_client) {
+ CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
+ crash_reporter_client->GetCrashDumpLocation(&database_path);
+
+ base::FilePath exe_file;
+ CHECK(PathService::Get(base::FILE_EXE, &exe_file));
+ base::string16 product_name, version, special_build, channel_name;
+ crash_reporter_client->GetProductNameAndVersion(
+ exe_file, &product_name, &version, &special_build, &channel_name);
+ std::map<std::string, std::string> process_annotations;
+ process_annotations["prod"] = base::UTF16ToUTF8(product_name);
+ process_annotations["ver"] = base::UTF16ToUTF8(version);
+ process_annotations["channel"] = base::UTF16ToUTF8(channel_name);
+ if (!special_build.empty())
+ process_annotations["special"] = base::UTF16ToUTF8(special_build);
+#if defined(ARCH_CPU_X86)
+ process_annotations["plat"] = std::string("Win32");
+#elif defined(ARCH_CPU_X86_64)
+ process_annotations["plat"] = std::string("Win64");
+#endif
+#if defined(GOOGLE_CHROME_BUILD)
+ std::string url = "https://clients2.google.com/cr/report";
+#else
+ std::string url;
+#endif
+
+ std::vector<std::string> arguments;
+
+ // In test binaries, use crashpad_handler directly. Otherwise, we launch
+ // chrome.exe with --type=crashpad-handler.
+ if (exe_file.BaseName().value() != FILE_PATH_LITERAL("chrome.exe")) {
+ base::FilePath exe_dir;
+ CHECK(PathService::Get(base::DIR_EXE, &exe_dir));
+ exe_file = exe_dir.Append(FILE_PATH_LITERAL("crashpad_handler.exe"));
+ } else {
+ arguments.push_back("--type=crashpad-handler");
+ }
+
+ result = g_crashpad_client.Get().StartHandler(
+ exe_file, database_path, url, process_annotations, arguments, false);
+
+ // If we're the browser, push the pipe name into the environment so child
+ // processes can connect to it. If we inherited another crashpad_handler's
+ // pipe name, we'll overwrite it here.
+ env->SetVar(kPipeNameVar,
+ base::UTF16ToUTF8(g_crashpad_client.Get().GetHandlerIPCPipe()));
+ } else {
+ std::string pipe_name_utf8;
+ result = env->GetVar(kPipeNameVar, &pipe_name_utf8);
+ if (result) {
+ result = g_crashpad_client.Get().SetHandlerIPCPipe(
+ base::UTF8ToUTF16(pipe_name_utf8));
+ }
+ }
+
+ if (result) {
+ result = g_crashpad_client.Get().UseHandler();
+ }
+
+ return database_path;
+}
+
+extern "C" {
+
+// 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!
+int __declspec(dllexport) CrashForException(
+ EXCEPTION_POINTERS* info) {
+ g_crashpad_client.Get().DumpAndCrash(info);
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+#if defined(ARCH_CPU_X86_64)
+
+static int CrashForExceptionInNonABICompliantCodeRange(
+ PEXCEPTION_RECORD ExceptionRecord,
+ ULONG64 EstablisherFrame,
+ PCONTEXT ContextRecord,
+ PDISPATCHER_CONTEXT DispatcherContext) {
+ EXCEPTION_POINTERS info = { ExceptionRecord, ContextRecord };
+ return CrashForException(&info);
+}
+
+// See https://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;
+
+struct ExceptionHandlerRecord {
+ RUNTIME_FUNCTION runtime_function;
+ UNWIND_INFO unwind_info;
+ unsigned char thunk[12];
+};
+
+// These are GetProcAddress()d from V8 binding code.
+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)));
+}
+
+void __declspec(dllexport) __cdecl UnregisterNonABICompliantCodeRange(
+ void* start) {
+ ExceptionHandlerRecord* record =
+ reinterpret_cast<ExceptionHandlerRecord*>(start);
+
+ CHECK(RtlDeleteFunctionTable(&record->runtime_function));
+}
+#endif // ARCH_CPU_X86_64
+
+} // extern "C"
+
+} // namespace internal
+} // namespace crash_reporter
diff --git a/components/crash/content/browser/BUILD.gn b/components/crash/content/browser/BUILD.gn
index b3a7948..850d622 100644
--- a/components/crash/content/browser/BUILD.gn
+++ b/components/crash/content/browser/BUILD.gn
@@ -30,7 +30,7 @@ source_set("browser") {
]
deps += [
"//breakpad:client",
- "//components/crash/content/app:app_non_mac",
+ "//components/crash/content/app:app_non_mac_win",
]
}
diff --git a/components/crash/core/common/crash_keys.cc b/components/crash/core/common/crash_keys.cc
index c0afc6a..3c3f2fb 100644
--- a/components/crash/core/common/crash_keys.cc
+++ b/components/crash/core/common/crash_keys.cc
@@ -16,6 +16,14 @@ namespace crash_keys {
// Crashpad owns the "guid" key. Chrome's metrics client ID is a separate ID
// carried in a distinct "metrics_client_id" field.
const char kMetricsClientId[] = "metrics_client_id";
+#elif defined(OS_WIN)
+// TODO(scottmg): While transitioning to Crashpad, there are some executables
+// that use Crashpad (which use kMetricsClientId), and some that use Breakpad
+// (kClientId), and they both use this file. For now we define both, but once
+// Breakpad is no longer used on Windows, we will no longer need kClientId, and
+// this can be combined with the OS_MACOSX block above.
+const char kMetricsClientId[] = "metrics_client_id";
+const char kClientId[] = "guid";
#else
const char kClientId[] = "guid";
#endif
@@ -44,7 +52,7 @@ void SetMetricsClientIdFromGUID(const std::string& metrics_client_guid) {
if (stripped_guid.empty())
return;
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_WIN)
// The crash client ID is maintained by Crashpad and is distinct from the
// metrics client ID, which is carried in its own key.
base::debug::SetCrashKeyValue(kMetricsClientId, stripped_guid);
@@ -56,7 +64,7 @@ void SetMetricsClientIdFromGUID(const std::string& metrics_client_guid) {
}
void ClearMetricsClientId() {
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_WIN)
// Crashpad always monitors for crashes, but doesn't upload them when
// crash reporting is disabled. The preference to upload crash reports is
// linked to the preference for metrics reporting. When metrics reporting is
diff --git a/components/crash/core/common/crash_keys.h b/components/crash/core/common/crash_keys.h
index 70b77ae..6ddb356 100644
--- a/components/crash/core/common/crash_keys.h
+++ b/components/crash/core/common/crash_keys.h
@@ -48,10 +48,13 @@ const size_t kLargeSize = kSmallSize * 16;
// The GUID used to identify this client to the crash system.
#if defined(OS_MACOSX)
-// On Mac OS X, the crash reporting client ID is the responsibility of Crashpad.
-// It is not set directly by Chrome. To make the metrics client ID available on
-// the server, it's stored in a distinct key.
+// When using Crashpad, the crash reporting client ID is the responsibility of
+// Crashpad. It is not set directly by Chrome. To make the metrics client ID
+// available on the server, it's stored in a distinct key.
extern const char kMetricsClientId[];
+#elif defined(OS_WIN)
+extern const char kMetricsClientId[];
+extern const char kClientId[];
#else
// When using Breakpad instead of Crashpad, the crash reporting client ID is the
// same as the metrics client ID.