Improve crashpad integration (issue #1995)

- Crash reporting is enabled and configured using a "crash_reporter.cfg"
  file. See comments in include/cef_crash_util.h and tools/crash_server.py
  for usage.
This commit is contained in:
Marshall Greenblatt 2016-12-12 11:05:29 +01:00
parent c990b5d877
commit a834487177
32 changed files with 2998 additions and 184 deletions

View File

@ -426,6 +426,8 @@ static_library("libcef_static") {
"libcef/common/content_client.h",
"libcef/common/crash_reporter_client.cc",
"libcef/common/crash_reporter_client.h",
"libcef/common/crash_reporting.cc",
"libcef/common/crash_reporting.h",
"libcef/common/drag_data_impl.cc",
"libcef/common/drag_data_impl.h",
"libcef/common/extensions/chrome_generated_schemas.cc",
@ -558,6 +560,11 @@ static_library("libcef_static") {
"//third_party/WebKit/public/web",
]
public_deps = [
# Bring in feature flag defines.
"//cef/libcef/features",
]
deps = [
# Generate pack files and associated CEF header files.
":make_pack_header_resources",
@ -679,6 +686,8 @@ static_library("libcef_static") {
"libcef/browser/osr/browser_platform_delegate_osr_win.cc",
"libcef/browser/osr/browser_platform_delegate_osr_win.h",
"libcef/browser/osr/render_widget_host_view_osr_win.cc",
"libcef/common/crash_reporting_win.cc",
"libcef/common/crash_reporting_win.h",
# Part of //chrome/utility.
"//chrome/utility/printing_handler.cc",
@ -687,7 +696,6 @@ static_library("libcef_static") {
deps += [
"//chrome_elf",
"//components/crash/content/app:run_as_crashpad_handler",
]
}
@ -730,6 +738,10 @@ static_library("libcef_static") {
]
}
if (is_win || is_mac) {
deps += [ "//third_party/crashpad/crashpad/handler:handler_lib" ]
}
if (use_x11) {
deps += [ "//ui/events/devices/x11" ]
}

View File

@ -21,6 +21,7 @@
'include/cef_command_line.h',
'include/cef_context_menu_handler.h',
'include/cef_cookie.h',
'include/cef_crash_util.h',
'include/cef_dialog_handler.h',
'include/cef_display_handler.h',
'include/cef_dom.h',
@ -108,6 +109,7 @@
'include/capi/cef_command_line_capi.h',
'include/capi/cef_context_menu_handler_capi.h',
'include/capi/cef_cookie_capi.h',
'include/capi/cef_crash_util_capi.h',
'include/capi/cef_dialog_handler_capi.h',
'include/capi/cef_display_handler_capi.h',
'include/capi/cef_dom_capi.h',

View File

@ -193,6 +193,8 @@
'tests/cefclient/browser/bytes_write_handler.cc',
'tests/cefclient/browser/bytes_write_handler.h',
'tests/cefclient/browser/client_app_delegates_browser.cc',
'tests/cefclient/browser/client_browser.cc',
'tests/cefclient/browser/client_browser.h',
'tests/cefclient/browser/client_handler.cc',
'tests/cefclient/browser/client_handler.h',
'tests/cefclient/browser/client_handler_osr.cc',

View File

@ -0,0 +1,136 @@
// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// ---------------------------------------------------------------------------
//
// This file was generated by the CEF translator tool and should not edited
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
#ifndef CEF_INCLUDE_CAPI_CEF_CRASH_UTIL_CAPI_H_
#define CEF_INCLUDE_CAPI_CEF_CRASH_UTIL_CAPI_H_
#pragma once
#include "include/capi/cef_base_capi.h"
#ifdef __cplusplus
extern "C" {
#endif
///
// Crash reporting is configured using an INI-style config file named
// "crash_reporter.cfg". On Windows and Linux this file must be placed next to
// the main application executable. On macOS this file must be placed in the
// top-level app bundle Resources directory (e.g.
// "<appname>.app/Contents/Resources"). File contents are as follows:
//
// # Comments start with a hash character and must be on their own line.
//
// [Config]
// AppName=<Windows only; App-specific folder name component for storing crash
// information; default to "CEF">
// ExternalHandler=<Windows only; Name of the external handler exe to use
// instead of re-launching the main exe; default to empty>
// ServerURL=<crash server URL; default to empty>
// RateLimitEnabled=<True if uploads should be rate limited; default to true>
// MaxUploadsPerDay=<Max uploads per 24 hours, used if rate limit is enabled;
// default to 5>
// MaxDatabaseSizeInMb=<Total crash report disk usage greater than this value
// will cause older reports to be deleted; default to 20>
// MaxDatabaseAgeInDays=<Crash reports older than this value will be deleted;
// default to 5>
//
// [CrashKeys]
// my_key1=<small|medium|large>
// my_key2=<small|medium|large>
//
// Config section:
//
// If "AppName" is set on Windows then crash report information (metrics,
// database and dumps) will be stored locally on disk under the
// "C:\Users\[CurrentUser]\AppData\Local\[AppName]\User Data" folder. On other
// platforms the CefSettings.user_data_path value will be used.
//
// If "ExternalHandler" is set on Windows then the specified exe will be
// launched as the crashpad-handler instead of re-launching the main process
// exe. The value can be an absolute path or a path relative to the main exe
// directory. On Linux the CefSettings.browser_subprocess_path value will be
// used. On macOS the existing subprocess app bundle will be used.
//
// If "ServerURL" is set then crashes will be uploaded as a multi-part POST
// request to the specified URL. Otherwise, reports will only be stored locally
// on disk.
//
// If "RateLimitEnabled" is set to true (1) then crash report uploads will be
// rate limited as follows:
// 1. If "MaxUploadsPerDay" is set to a positive value then at most the
// specified number of crashes will be uploaded in each 24 hour period.
// 2. If crash upload fails due to a network or server error then an
// incremental backoff delay up to a maximum of 24 hours will be applied for
// retries.
// 3. If a backoff delay is applied and "MaxUploadsPerDay" is > 1 then the
// "MaxUploadsPerDay" value will be reduced to 1 until the client is
// restarted. This helps to avoid an upload flood when the network or
// server error is resolved.
// Rate limiting is not supported on Linux.
//
// If "MaxDatabaseSizeInMb" is set to a positive value then crash report storage
// on disk will be limited to that size in megabytes. For example, on Windows
// each dump is about 600KB so a "MaxDatabaseSizeInMb" value of 20 equates to
// about 34 crash reports stored on disk. Not supported on Linux.
//
// If "MaxDatabaseAgeInDays" is set to a positive value then crash reports older
// than the specified age in days will be deleted. Not supported on Linux.
//
// CrashKeys section:
//
// Any number of crash keys can be specified for use by the application. Crash
// key values will be truncated based on the specified size (small = 63 bytes,
// medium = 252 bytes, large = 1008 bytes). The value of crash keys can be set
// from any thread or process using the CefSetCrashKeyValue function. These
// key/value pairs will be sent to the crash server along with the crash dump
// file. Medium and large values will be chunked for submission. For example, if
// your key is named "mykey" then the value will be broken into ordered chunks
// and submitted using keys named "mykey-1", "mykey-2", etc.
///
CEF_EXPORT int cef_crash_reporting_enabled();
///
// Sets or clears a specific key-value pair from the crash metadata.
///
CEF_EXPORT void cef_set_crash_key_value(const cef_string_t* key,
const cef_string_t* value);
#ifdef __cplusplus
}
#endif
#endif // CEF_INCLUDE_CAPI_CEF_CRASH_UTIL_CAPI_H_

128
include/cef_crash_util.h Normal file
View File

@ -0,0 +1,128 @@
// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// ---------------------------------------------------------------------------
//
// The contents of this file must follow a specific format in order to
// support the CEF translator tool. See the translator.README.txt file in the
// tools directory for more information.
//
#ifndef CEF_INCLUDE_CEF_CRASH_UTIL_H_
#define CEF_INCLUDE_CEF_CRASH_UTIL_H_
#pragma once
///
// Crash reporting is configured using an INI-style config file named
// "crash_reporter.cfg". On Windows and Linux this file must be placed next to
// the main application executable. On macOS this file must be placed in the
// top-level app bundle Resources directory (e.g.
// "<appname>.app/Contents/Resources"). File contents are as follows:
//
// # Comments start with a hash character and must be on their own line.
//
// [Config]
// AppName=<Windows only; App-specific folder name component for storing crash
// information; default to "CEF">
// ExternalHandler=<Windows only; Name of the external handler exe to use
// instead of re-launching the main exe; default to empty>
// ServerURL=<crash server URL; default to empty>
// RateLimitEnabled=<True if uploads should be rate limited; default to true>
// MaxUploadsPerDay=<Max uploads per 24 hours, used if rate limit is enabled;
// default to 5>
// MaxDatabaseSizeInMb=<Total crash report disk usage greater than this value
// will cause older reports to be deleted; default to 20>
// MaxDatabaseAgeInDays=<Crash reports older than this value will be deleted;
// default to 5>
//
// [CrashKeys]
// my_key1=<small|medium|large>
// my_key2=<small|medium|large>
//
// Config section:
//
// If "AppName" is set on Windows then crash report information (metrics,
// database and dumps) will be stored locally on disk under the
// "C:\Users\[CurrentUser]\AppData\Local\[AppName]\User Data" folder. On other
// platforms the CefSettings.user_data_path value will be used.
//
// If "ExternalHandler" is set on Windows then the specified exe will be
// launched as the crashpad-handler instead of re-launching the main process
// exe. The value can be an absolute path or a path relative to the main exe
// directory. On Linux the CefSettings.browser_subprocess_path value will be
// used. On macOS the existing subprocess app bundle will be used.
//
// If "ServerURL" is set then crashes will be uploaded as a multi-part POST
// request to the specified URL. Otherwise, reports will only be stored locally
// on disk.
//
// If "RateLimitEnabled" is set to true then crash report uploads will be rate
// limited as follows:
// 1. If "MaxUploadsPerDay" is set to a positive value then at most the
// specified number of crashes will be uploaded in each 24 hour period.
// 2. If crash upload fails due to a network or server error then an
// incremental backoff delay up to a maximum of 24 hours will be applied for
// retries.
// 3. If a backoff delay is applied and "MaxUploadsPerDay" is > 1 then the
// "MaxUploadsPerDay" value will be reduced to 1 until the client is
// restarted. This helps to avoid an upload flood when the network or
// server error is resolved.
// Rate limiting is not supported on Linux.
//
// If "MaxDatabaseSizeInMb" is set to a positive value then crash report storage
// on disk will be limited to that size in megabytes. For example, on Windows
// each dump is about 600KB so a "MaxDatabaseSizeInMb" value of 20 equates to
// about 34 crash reports stored on disk. Not supported on Linux.
//
// If "MaxDatabaseAgeInDays" is set to a positive value then crash reports older
// than the specified age in days will be deleted. Not supported on Linux.
//
// CrashKeys section:
//
// Any number of crash keys can be specified for use by the application. Crash
// key values will be truncated based on the specified size (small = 63 bytes,
// medium = 252 bytes, large = 1008 bytes). The value of crash keys can be set
// from any thread or process using the CefSetCrashKeyValue function. These
// key/value pairs will be sent to the crash server along with the crash dump
// file. Medium and large values will be chunked for submission. For example,
// if your key is named "mykey" then the value will be broken into ordered
// chunks and submitted using keys named "mykey-1", "mykey-2", etc.
///
/*--cef()--*/
bool CefCrashReportingEnabled();
#include "include/cef_base.h"
///
// Sets or clears a specific key-value pair from the crash metadata.
///
/*--cef()--*/
void CefSetCrashKeyValue(const CefString& key, const CefString& value);
#endif // CEF_INCLUDE_CEF_CRASH_UTIL_H_

View File

@ -89,6 +89,7 @@
#if defined(OS_POSIX) && !defined(OS_MACOSX)
#include "base/debug/leak_annotations.h"
#include "chrome/common/chrome_paths.h"
#include "components/crash/content/app/breakpad_linux.h"
#include "components/crash/content/browser/crash_handler_host_linux.h"
#include "content/public/common/content_descriptors.h"
@ -317,14 +318,15 @@ class CefQuotaPermissionContext : public content::QuotaPermissionContext {
#if defined(OS_POSIX) && !defined(OS_MACOSX)
breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost(
const std::string& process_type) {
base::FilePath dumps_path =
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kCrashDumpsDir);
base::FilePath dumps_path;
PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path);
{
ANNOTATE_SCOPED_MEMORY_LEAK;
// Uploads will only occur if a non-empty crash URL is specified in
// CefMainDelegate::InitCrashReporter.
breakpad::CrashHandlerHostLinux* crash_handler =
new breakpad::CrashHandlerHostLinux(
process_type, dumps_path, false);
process_type, dumps_path, true /* upload */);
crash_handler->StartUploaderThread();
return crash_handler;
}
@ -618,11 +620,7 @@ void CefContentBrowserClient::AppendExtraCommandLineSwitches(
// Propagate the following switches to all command lines (along with any
// associated values) if present in the browser command line.
static const char* const kSwitchNames[] = {
#if !defined(OS_WIN)
switches::kCrashDumpsDir,
#endif
switches::kDisablePackLoading,
switches::kEnableCrashReporter,
switches::kLang,
switches::kLocalesDirPath,
switches::kLogFile,

View File

@ -31,14 +31,18 @@
#include "ui/base/ui_base_switches.h"
#if defined(OS_WIN)
#include "base/strings/utf_string_conversions.h"
#include "chrome_elf/chrome_elf_main.h"
#include "content/public/app/sandbox_helper_win.h"
#include "components/crash/content/app/crash_switches.h"
#include "components/crash/content/app/crashpad.h"
#include "components/crash/content/app/run_as_crashpad_handler_win.h"
#include "sandbox/win/src/sandbox_types.h"
#endif
#if defined(OS_MACOSX) || defined(OS_WIN)
#include "components/crash/content/app/crash_switches.h"
#include "third_party/crashpad/crashpad/handler/handler_main.h"
#endif
namespace {
CefContext* g_context = NULL;
@ -73,7 +77,7 @@ void DisableFMA3() {
// Signal chrome_elf to initialize crash reporting, rather than doing it in
// DllMain. See https://crbug.com/656800 for details.
void InitializeCrashReporting() {
void InitCrashReporter() {
static bool initialized = false;
if (initialized)
return;
@ -82,6 +86,47 @@ void InitializeCrashReporting() {
}
#endif // defined(OS_WIN)
#if defined(OS_MACOSX) || defined(OS_WIN)
// Based on components/crash/content/app/run_as_crashpad_handler_win.cc
// Remove the "--type=crashpad-handler" command-line flag that will otherwise
// confuse the crashpad handler.
// Chrome uses an embedded crashpad handler on Windows only and imports this
// function via the existing "run_as_crashpad_handler" target defined in
// components/crash/content/app/BUILD.gn. CEF uses an embedded handler on both
// Windows and macOS so we define the function here instead of using the
// existing target (because we can't use that target on macOS).
int RunAsCrashpadHandler(const base::CommandLine& command_line) {
base::CommandLine::StringVector argv = command_line.argv();
const base::CommandLine::StringType process_type =
FILE_PATH_LITERAL("--type=");
argv.erase(std::remove_if(argv.begin(), argv.end(),
[&process_type](const base::CommandLine::StringType& str) {
return base::StartsWith(str, process_type,
base::CompareCase::SENSITIVE) ||
(!str.empty() && str[0] == L'/');
}),
argv.end());
std::unique_ptr<char* []> argv_as_utf8(new char*[argv.size() + 1]);
std::vector<std::string> storage;
storage.reserve(argv.size());
for (size_t i = 0; i < argv.size(); ++i) {
#if defined(OS_WIN)
storage.push_back(base::UTF16ToUTF8(argv[i]));
#else
storage.push_back(argv[i]);
#endif
argv_as_utf8[i] = &storage[i][0];
}
argv_as_utf8[argv.size()] = nullptr;
argv.clear();
return crashpad::HandlerMain(static_cast<int>(storage.size()),
argv_as_utf8.get());
}
#endif // defined(OS_MACOSX) || defined(OS_WIN)
} // namespace
int CefExecuteProcess(const CefMainArgs& args,
@ -91,7 +136,7 @@ int CefExecuteProcess(const CefMainArgs& args,
#if defined(ARCH_CPU_X86_64)
DisableFMA3();
#endif
InitializeCrashReporting();
InitCrashReporter();
#endif
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
@ -112,9 +157,9 @@ int CefExecuteProcess(const CefMainArgs& args,
if (process_type.empty())
return -1;
#if defined(OS_WIN)
#if defined(OS_MACOSX) || defined(OS_WIN)
if (process_type == crash_reporter::switches::kCrashpadHandler)
return crash_reporter::RunAsCrashpadHandler(command_line);
return RunAsCrashpadHandler(command_line);
#endif
CefMainDelegate main_delegate(application);
@ -150,7 +195,7 @@ bool CefInitialize(const CefMainArgs& args,
#if defined(ARCH_CPU_X86_64)
DisableFMA3();
#endif
InitializeCrashReporting();
InitCrashReporter();
#endif
// Return true if the global context already exists.

View File

@ -32,6 +32,7 @@
#include "chrome/browser/browser_about_handler.h"
#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
#include "chrome/common/url_constants.h"
#include "content/browser/frame_host/debug_urls.h"
#include "content/browser/webui/content_web_ui_controller_factory.h"
#include "content/public/browser/browser_url_handler.h"
#include "content/public/common/url_constants.h"
@ -74,6 +75,13 @@ const char* kAllowedWebUIHosts[] = {
content::kChromeUIWebRTCInternalsHost,
};
// Hosts that don't have useful output when linked directly. They'll be excluded
// from the "chrome://webui-hosts" listing.
const char* kUnlistedHosts[] = {
content::kChromeUINetworkErrorHost,
content::kChromeUIResourcesHost,
};
enum ChromeHostId {
CHROME_UNKNOWN = 0,
CHROME_LICENSE,
@ -103,18 +111,87 @@ ChromeHostId GetChromeHostId(const std::string& host) {
return CHROME_UNKNOWN;
}
// Returns CEF and WebUI hosts. Does not include chrome debug hosts (for
// crashing, etc).
void GetAllowedHosts(std::vector<std::string>* hosts) {
// Hosts implemented by CEF.
for (size_t i = 0;
i < sizeof(kAllowedCefHosts) / sizeof(kAllowedCefHosts[0]); ++i) {
hosts->push_back(kAllowedCefHosts[i].host);
}
// Explicitly whitelisted WebUI hosts.
for (size_t i = 0;
i < sizeof(kAllowedWebUIHosts) / sizeof(kAllowedWebUIHosts[0]); ++i) {
hosts->push_back(kAllowedWebUIHosts[i]);
}
}
// Returns true if a host should not be listed on "chrome://webui-hosts".
bool IsUnlistedHost(const std::string& host) {
for (size_t i = 0;
i < sizeof(kUnlistedHosts) / sizeof(kUnlistedHosts[0]); ++i) {
if (host == kUnlistedHosts[i])
return true;
}
return false;
}
// Returns true if a host is WebUI and should be allowed to load.
bool IsAllowedWebUIHost(const std::string& host) {
// Explicitly whitelisted WebUI hosts.
for (size_t i = 0;
i < sizeof(kAllowedWebUIHosts) / sizeof(kAllowedWebUIHosts[0]); ++i) {
if (base::EqualsCaseInsensitiveASCII(kAllowedWebUIHosts[i],
host.c_str())) {
return true;
}
}
return false;
}
// Additional debug URLs that are not included in chrome::kChromeDebugURLs.
const char* kAllowedDebugURLs[] = {
content::kChromeUIBrowserCrashURL,
};
// Returns true for debug URLs that receive special handling (for crashes, etc).
bool IsDebugURL(const GURL& url) {
// URLs handled by the renderer process in
// content/renderer/render_frame_impl.cc MaybeHandleDebugURL().
if (content::IsRendererDebugURL(url))
return true;
// Also include URLs handled by the browser process in
// content/browser/frame_host/debug_urls.cc HandleDebugURL().
for (int i = 0; i < chrome::kNumberOfChromeDebugURLs; ++i) {
GURL host(chrome::kChromeDebugURLs[i]);
if (url.GetOrigin() == host.GetOrigin())
return true;
}
for (size_t i = 0;
i < sizeof(kAllowedDebugURLs) / sizeof(kAllowedDebugURLs[0]); ++i) {
GURL host(kAllowedDebugURLs[i]);
if (url.GetOrigin() == host.GetOrigin())
return true;
}
return false;
}
void GetDebugURLs(std::vector<std::string>* urls) {
for (int i = 0; i < chrome::kNumberOfChromeDebugURLs; ++i) {
urls->push_back(chrome::kChromeDebugURLs[i]);
}
for (size_t i = 0;
i < sizeof(kAllowedDebugURLs) / sizeof(kAllowedDebugURLs[0]); ++i) {
urls->push_back(kAllowedDebugURLs[i]);
}
}
// Intercepts all WebUI calls and either blocks them or forwards them to the
// Content or Chrome WebUI factory as appropriate.
class CefWebUIControllerFactory : public content::WebUIControllerFactory {
@ -124,13 +201,8 @@ class CefWebUIControllerFactory : public content::WebUIControllerFactory {
if (!url.SchemeIs(content::kChromeUIScheme))
return false;
for (size_t i = 0;
i < sizeof(kAllowedWebUIHosts) / sizeof(kAllowedWebUIHosts[0]); ++i) {
if (base::EqualsCaseInsensitiveASCII(kAllowedWebUIHosts[i],
url.host().c_str())) {
return true;
}
}
if (IsAllowedWebUIHost(url.host()))
return true;
return false;
}
@ -534,16 +606,32 @@ class Delegate : public InternalHandlerDelegate {
std::string html = "<html>\n<head><title>WebUI Hosts</title></head>\n"
"<body bgcolor=\"white\"><h3>WebUI Hosts</h3>\n<ul>\n";
std::vector<std::string> hosts;
GetAllowedHosts(&hosts);
std::sort(hosts.begin(), hosts.end());
std::vector<std::string> list;
GetAllowedHosts(&list);
std::sort(list.begin(), list.end());
for (size_t i = 0U; i < hosts.size(); ++i) {
html += "<li><a href=\"chrome://" + hosts[i] + "\">chrome://" +
hosts[i] + "</a></li>\n";
for (size_t i = 0U; i < list.size(); ++i) {
if (IsUnlistedHost(list[i]))
continue;
html += "<li><a href=\"chrome://" + list[i] + "\">chrome://" +
list[i] + "</a></li>\n";
}
html += "</ul></body>\n</html>";
list.clear();
GetDebugURLs(&list);
std::sort(list.begin(), list.end());
html += "</ul>\n<h3>For Debug</h3>\n"
"<p>The following pages are for debugging purposes only. Because they "
"crash or hang the renderer, they're not linked directly; you can type "
"them into the address bar if you need them.</p>\n<ul>\n";
for (size_t i = 0U; i < list.size(); ++i) {
html += "<li>" + std::string(list[i]) + "</li>\n";
}
html += "</ul>\n";
html += "</body>\n</html>";
action->mime_type = "text/html";
action->stream = CefStreamReader::CreateForData(
@ -604,6 +692,10 @@ class ChromeProtocolHandlerWrapper :
net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override {
// Don't handle debug URLs.
if (IsDebugURL(request->url()))
return nullptr;
// Only allow WebUI to handle chrome:// URLs whitelisted by CEF.
if (CefWebUIControllerFactory::AllowWebUIForURL(request->url())) {
return chrome_protocol_handler_->MaybeCreateJob(request,

View File

@ -0,0 +1,247 @@
// Copyright 2016 The Chromium Embedded Framework 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 "libcef/common/cef_crash_report_upload_thread.h"
#include "third_party/crashpad/crashpad/client/settings.h"
using namespace crashpad;
namespace {
// Calls CrashReportDatabase::RecordUploadAttempt() with |successful| set to
// false upon destruction unless disarmed by calling Fire() or Disarm(). Fire()
// triggers an immediate call. Armed upon construction.
class CallRecordUploadAttempt {
public:
CallRecordUploadAttempt(CrashReportDatabase* database,
const CrashReportDatabase::Report* report)
: database_(database),
report_(report) {
}
~CallRecordUploadAttempt() {
Fire();
}
void Fire() {
if (report_) {
database_->RecordUploadAttempt(report_, false, std::string());
}
Disarm();
}
void Disarm() {
report_ = nullptr;
}
private:
CrashReportDatabase* database_; // weak
const CrashReportDatabase::Report* report_; // weak
DISALLOW_COPY_AND_ASSIGN(CallRecordUploadAttempt);
};
} // namespace
CefCrashReportUploadThread::CefCrashReportUploadThread(
CrashReportDatabase* database,
const std::string& url,
bool rate_limit,
int max_uploads)
: CrashReportUploadThread(database, url, rate_limit),
max_uploads_(max_uploads) {
}
CefCrashReportUploadThread::~CefCrashReportUploadThread() {
}
void CefCrashReportUploadThread::ProcessPendingReports() {
if (BackoffPending()) {
// Try again later.
return;
}
if (MaxUploadsEnabled()) {
// Retrieve all completed reports.
std::vector<CrashReportDatabase::Report> reports;
if (database_->GetCompletedReports(&reports) !=
CrashReportDatabase::kNoError) {
// The database is sick. It might be prudent to stop trying to poke it
// from this thread by abandoning the thread altogether. On the other
// hand, if the problem is transient, it might be possible to talk to it
// again on the next pass. For now, take the latter approach.
return;
}
const time_t now = time(nullptr);
const int kSeconds = 60 * 60 * 24; // 24 hours
// Count how many reports have completed in the last 24 hours.
recent_upload_ct_ = 0;
for (const CrashReportDatabase::Report& report : reports) {
if (report.last_upload_attempt_time > now - kSeconds)
recent_upload_ct_++;
}
}
// Continue with processing pending reports.
CrashReportUploadThread::ProcessPendingReports();
}
void CefCrashReportUploadThread::ProcessPendingReport(
const CrashReportDatabase::Report& report) {
// Always allow upload if it's been explicitly requested by the user.
if (!report.upload_explicitly_requested) {
if (!UploadsEnabled()) {
// Dont attempt an upload if theres no URL or if uploads have been
// disabled in the databases settings.
database_->SkipReportUpload(
report.uuid, Metrics::CrashSkippedReason::kUploadsDisabled);
return;
}
if (MaxUploadsExceeded()) {
// Don't send uploads if the rate limit has been exceeded.
database_->SkipReportUpload(
report.uuid, Metrics::CrashSkippedReason::kUploadThrottled);
return;
}
}
if (BackoffPending()) {
// Try again later.
return;
}
const CrashReportDatabase::Report* upload_report;
CrashReportDatabase::OperationStatus status =
database_->GetReportForUploading(report.uuid, &upload_report);
switch (status) {
case CrashReportDatabase::kNoError:
break;
case CrashReportDatabase::kBusyError:
return;
case CrashReportDatabase::kReportNotFound:
case CrashReportDatabase::kFileSystemError:
case CrashReportDatabase::kDatabaseError:
// In these cases, SkipReportUpload() might not work either, but its best
// to at least try to get the report out of the way.
database_->SkipReportUpload(report.uuid,
Metrics::CrashSkippedReason::kDatabaseError);
return;
case CrashReportDatabase::kCannotRequestUpload:
NOTREACHED();
return;
}
CallRecordUploadAttempt call_record_upload_attempt(database_, upload_report);
std::string response_body;
UploadResult upload_result = UploadReport(upload_report, &response_body);
switch (upload_result) {
case UploadResult::kSuccess:
// The upload completed successfully.
call_record_upload_attempt.Disarm();
database_->RecordUploadAttempt(upload_report, true, response_body);
if (MaxUploadsEnabled())
recent_upload_ct_++;
ResetBackoff();
break;
case UploadResult::kPermanentFailure:
// The upload should never be retried.
call_record_upload_attempt.Fire();
database_->SkipReportUpload(report.uuid,
Metrics::CrashSkippedReason::kUploadFailed);
break;
case UploadResult::kRetry:
// The upload will be retried after a reasonable backoff delay. Since we
// didn't successfully upload it we won't count it against the rate limit.
IncreaseBackoff();
break;
}
}
bool CefCrashReportUploadThread::UploadsEnabled() const {
Settings* const settings = database_->GetSettings();
bool uploads_enabled;
return !url_.empty() &&
settings->GetUploadsEnabled(&uploads_enabled) && uploads_enabled;
}
bool CefCrashReportUploadThread::MaxUploadsEnabled() const {
return rate_limit_ && max_uploads_ > 0;
}
bool CefCrashReportUploadThread::MaxUploadsExceeded() const {
return MaxUploadsEnabled() && recent_upload_ct_ >= max_uploads_;
}
bool CefCrashReportUploadThread::BackoffPending() const {
if (!rate_limit_)
return false;
Settings* const settings = database_->GetSettings();
time_t next_upload_time;
if (settings->GetNextUploadAttemptTime(&next_upload_time) &&
next_upload_time > 0) {
const time_t now = time(nullptr);
if (now < next_upload_time)
return true;
}
return false;
}
void CefCrashReportUploadThread::IncreaseBackoff() {
if (!rate_limit_)
return;
const int kHour = 60 * 60; // 1 hour
const int kBackoffSchedule[] = {
kHour / 4, // 15 minutes
kHour, // 1 hour
kHour * 2, // 2 hours
kHour * 4, // 4 hours
kHour * 8, // 8 hours
kHour * 24, // 24 hours
};
const int kBackoffScheduleSize =
sizeof(kBackoffSchedule) / sizeof(kBackoffSchedule[0]);
Settings* settings = database_->GetSettings();
int backoff_step = 0;
if (settings->GetBackoffStep(&backoff_step) && backoff_step < 0)
backoff_step = 0;
if (++backoff_step > kBackoffScheduleSize)
backoff_step = kBackoffScheduleSize;
time_t next_upload_time = time(nullptr); // now
next_upload_time += kBackoffSchedule[backoff_step - 1];
settings->SetBackoffStep(backoff_step);
settings->SetNextUploadAttemptTime(next_upload_time);
if (max_uploads_ > 1) {
// If the server is having trouble then we don't want to send many crash
// reports after the backoff expires. Reduce max uploads to 1 per 24 hours
// until the client is restarted.
max_uploads_ = 1;
}
}
void CefCrashReportUploadThread::ResetBackoff() {
if (!rate_limit_)
return;
Settings* settings = database_->GetSettings();
settings->SetBackoffStep(0);
settings->SetNextUploadAttemptTime(0);
}

View File

@ -0,0 +1,42 @@
// Copyright 2016 The Chromium Embedded Framework 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 CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
#define CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
#include "third_party/crashpad/crashpad/handler/crash_report_upload_thread.h"
class CefCrashReportUploadThread : public crashpad::CrashReportUploadThread {
public:
CefCrashReportUploadThread(crashpad::CrashReportDatabase* database,
const std::string& url,
bool rate_limit,
int max_uploads);
~CefCrashReportUploadThread();
private:
void ProcessPendingReports() override;
void ProcessPendingReport(
const crashpad::CrashReportDatabase::Report& report) override;
bool UploadsEnabled() const;
bool MaxUploadsEnabled() const;
bool MaxUploadsExceeded() const;
bool BackoffPending() const;
void IncreaseBackoff();
void ResetBackoff();
int max_uploads_;
// Track the number of uploads that have completed within the last 24 hours.
// Only used when RateLimitEnabled() is true. Value is reset each time
// ProcessPendingReports() is called.
int recent_upload_ct_ = 0;
DISALLOW_COPY_AND_ASSIGN(CefCrashReportUploadThread);
};
#endif // CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_

View File

@ -82,9 +82,6 @@ const char kEnableSpeechInput[] = "enable-speech-input";
// Enable the speech input profanity filter.
const char kEnableProfanityFilter[] = "enable-profanity-filter";
// The directory breakpad should store minidumps in.
const char kCrashDumpsDir[] = "crash-dumps-dir";
// Disable spell checking.
const char kDisableSpellChecking[] = "disable-spell-checking";

View File

@ -37,7 +37,6 @@ extern const char kPersistUserPreferences[];
extern const char kEnableMediaStream[];
extern const char kEnableSpeechInput[];
extern const char kEnableProfanityFilter[];
extern const char kCrashDumpsDir[];
extern const char kDisableSpellChecking[];
extern const char kEnableSpellingService[];
extern const char kOverrideSpellCheckLang[];

View File

@ -1,26 +1,465 @@
// 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.
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2016 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 "libcef/common/crash_reporter_client.h"
#include <algorithm>
#include <iterator>
#include <utility>
#include "libcef/common/cef_switches.h"
#include "include/cef_version.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
#include "base/command_line.h"
#include "base/logging.h"
#include "base/files/file_path.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/crash_keys.h"
#if defined(OS_MACOSX)
#include "base/mac/foundation_util.h"
#else
#include "include/cef_version.h"
#endif
#if defined(OS_POSIX)
// Don't use CommandLine, FilePath or PathService on Windows. FilePath has
// dependencies outside of kernel32, which is disallowed by chrome_elf.
// CommandLine and PathService depend on global state that will not be
// initialized at the time the CefCrashReporterClient object is created.
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#endif
#if defined(OS_POSIX) && !defined(OS_MACOSX)
#include "content/public/common/content_switches.h"
#endif
#if defined(OS_WIN)
#include "base/debug/leak_annotations.h"
#include "chrome/install_static/install_util.h"
#include "components/crash/content/app/crashpad.h"
#endif
namespace {
#if defined(OS_WIN)
typedef base::string16 PathString;
const char kPathSep = '\\';
#else
typedef std::string PathString;
const char kPathSep = '/';
#endif
PathString GetCrashConfigPath() {
#if defined(OS_WIN)
// Start with the path to the running executable.
wchar_t module_path[MAX_PATH];
if (GetModuleFileName(nullptr, module_path, MAX_PATH) == 0)
return PathString();
PathString config_path = module_path;
// Remove the executable file name.
PathString::size_type last_backslash =
config_path.rfind(kPathSep, config_path.size());
if (last_backslash != PathString::npos)
config_path.erase(last_backslash + 1);
config_path += L"crash_reporter.cfg";
return config_path;
#elif defined(OS_POSIX)
// Start with the path to the running executable.
base::FilePath config_path;
if (!PathService::Get(base::DIR_EXE, &config_path))
return PathString();
#if defined(OS_MACOSX)
// Get the main app bundle path.
config_path = base::mac::GetAppBundlePath(config_path);
if (config_path.empty())
return PathString();
// Go into the Contents/Resources directory.
config_path = config_path.Append(FILE_PATH_LITERAL("Contents"))
.Append(FILE_PATH_LITERAL("Resources"));
#endif
return config_path.Append(FILE_PATH_LITERAL("crash_reporter.cfg")).value();
#endif // defined(OS_POSIX)
}
// On Windows, FAT32 and NTFS both limit filenames to a maximum of 255
// characters. On POSIX systems, the typical filename length limit is 255
// character units. HFS+'s limit is actually 255 Unicode characters using
// Apple's modification of Normalization Form D, but the differences aren't
// really worth dealing with here.
const unsigned maxFilenameLength = 255;
#if defined(OS_WIN)
const char kInvalidFileChars[] = "<>:\"/\\|?*";
bool isInvalidFileCharacter(unsigned char c) {
if (c < ' ' || c == 0x7F)
return true;
for(size_t i = 0; i < sizeof(kInvalidFileChars); ++i) {
if (c == kInvalidFileChars[i])
return true;
}
return false;
}
bool isAbsolutePath(const std::string& s) {
// Check for local paths (beginning with "c:\") and network paths
// (beginning with "\\").
return s.length() > 2 &&
((isalpha(s[0]) && s[1] == ':' && s[2] == kPathSep) ||
(s[0] == kPathSep && s[1] == kPathSep));
}
std::string extractAbsolutePathStart(std::string& s) {
if (!isAbsolutePath(s))
return std::string();
std::string start;
if (s[0] == kPathSep) {
// Network path.
start = s.substr(0, 2);
s = s.substr(2);
} else {
// Local path.
start = s.substr(0, 3);
s = s.substr(3);
}
return start;
}
#elif defined(OS_POSIX)
bool isInvalidFileCharacter(unsigned char c) {
// HFS+ disallows '/' and Linux systems also disallow null. For sanity's sake
// we'll also disallow control characters.
return c < ' ' || c == 0x7F || c == kPathSep;
}
bool isAbsolutePath(const std::string& s) {
// Check for local paths (beginning with "/") and network paths (beginning
// with "//").
return s.length() > 1 && s[0] == kPathSep;
}
std::string extractAbsolutePathStart(std::string& s) {
if (!isAbsolutePath(s))
return std::string();
// May have multiple '/' at the beginning of the path.
std::string start;
do {
s = s.substr(1);
start.push_back(kPathSep);
} while (s.length() > 0 && s[0] == kPathSep);
return start;
}
#endif // defined(OS_POSIX)
std::string sanitizePathComponentPart(const std::string& s) {
if (s.empty())
return std::string();
std::string result;
result.reserve(s.length());
std::remove_copy_if(s.begin(), s.end(),
std::back_inserter(result),
std::not1(std::ptr_fun(isInvalidFileCharacter)));
return result;
}
std::string sanitizePathComponent(const std::string& s) {
std::string name, ext;
// Separate name and extension, if any.
std::string::size_type pos = s.rfind('.');
if (pos != std::string::npos) {
name = s.substr(0, pos);
ext = s.substr(pos + 1);
} else {
name = s;
}
// Remove invalid characters.
name = sanitizePathComponentPart(name);
ext = sanitizePathComponentPart(ext);
// Remove a ridiculously-long extension.
if (ext.length() >= maxFilenameLength)
ext = std::string();
// Truncate an overly-long filename, reserving one character for a dot.
std::string::size_type max_name_len = maxFilenameLength - ext.length() - 1;
if (name.length() > max_name_len)
name = name.substr(0, max_name_len);
return ext.empty() ? name : name + "." + ext;
}
std::string sanitizePath(const std::string& s) {
std::string path = s;
// Extract the absolute path start component, if any (e.g. "c:\" on Windows).
std::string result = extractAbsolutePathStart(path);
result.reserve(s.length());
std::vector<std::string> parts =
base::SplitString(path, std::string() + kPathSep, base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
for (size_t i = 0; i < parts.size(); ++i) {
std::string part = parts[i];
if (part != "." && part != "..")
part = sanitizePathComponent(part);
if (!result.empty() && result[result.length()-1] != kPathSep)
result += kPathSep;
result += part;
}
return result;
}
std::string joinPath(const std::string& s1, const std::string& s2) {
if (s1.empty() && s2.empty())
return std::string();
if (s1.empty())
return s2;
if (s2.empty())
return s1;
#if defined(OS_WIN)
// Don't try to join absolute paths on Windows.
// Skip this check on POSIX where it's more difficult to differentiate.
if (isAbsolutePath(s2))
return s2;
#endif
std::string result = s1;
if (result[result.size() - 1] != kPathSep)
result += kPathSep;
if (s2[0] == kPathSep)
result += s2.substr(1);
else
result += s2;
return result;
}
#if defined(OS_WIN)
// This will only be non-nullptr in the chrome_elf address space.
CefCrashReporterClient* g_crash_reporter_client = nullptr;
#endif
} // namespace
#if defined(OS_WIN)
extern "C" {
// Export functions from chrome_elf that are required by
// crash_reporting_win::InitializeCrashReportingForModule().
size_t __declspec(dllexport) __cdecl GetCrashKeyCountImpl() {
if (!g_crash_reporter_client)
return 0;
return g_crash_reporter_client->GetCrashKeyCount();
}
bool __declspec(dllexport) __cdecl GetCrashKeyImpl(size_t index,
const char** key_name,
size_t* max_length) {
if (!g_crash_reporter_client)
return false;
return g_crash_reporter_client->GetCrashKey(index, key_name, max_length);
}
} // extern "C"
#endif // OS_WIN
CefCrashReporterClient::CefCrashReporterClient() {}
CefCrashReporterClient::~CefCrashReporterClient() {}
// Be aware that logging is not initialized at the time this method is called.
bool CefCrashReporterClient::ReadCrashConfigFile() {
if (has_crash_config_file_)
return true;
PathString config_path = GetCrashConfigPath();
if (config_path.empty())
return false;
#if defined(OS_WIN)
FILE* fp = _wfopen(config_path.c_str(), L"r");
#else
FILE* fp = fopen(config_path.c_str(), "r");
#endif
if (!fp)
return false;
char line[1000];
enum section {
kNoSection,
kConfigSection,
kCrashKeysSection,
} current_section = kNoSection;
while (fgets(line, sizeof(line) - 1, fp) != NULL) {
std::string str = line;
base::TrimString(str, base::kWhitespaceASCII, &str);
if (str.empty() || str[0] == '#')
continue;
if (str == "[Config]") {
current_section = kConfigSection;
continue;
} else if (str == "[CrashKeys]") {
current_section = kCrashKeysSection;
continue;
} else if (str[0] == '[') {
current_section = kNoSection;
continue;
}
if (current_section == kNoSection)
continue;
size_t div = str.find('=');
if (div == std::string::npos)
continue;
std::string name_str = str.substr(0, div);
base::TrimString(name_str, base::kWhitespaceASCII, &name_str);
std::string val_str = str.substr(div + 1);
base::TrimString(val_str, base::kWhitespaceASCII, &val_str);
if (name_str.empty() || val_str.empty())
continue;
if (current_section == kConfigSection) {
if (name_str == "ServerURL") {
if (val_str.find("http://") == 0 || val_str.find("https://") == 0)
server_url_ = val_str;
} else if (name_str == "RateLimitEnabled") {
rate_limit_ = (base::EqualsCaseInsensitiveASCII(val_str, "true") ||
val_str == "1");
} else if (name_str == "MaxUploadsPerDay") {
if (base::StringToInt(val_str, &max_uploads_)) {
if (max_uploads_ < 0)
max_uploads_ = 0;
}
} else if (name_str == "MaxDatabaseSizeInMb") {
if (base::StringToInt(val_str, &max_db_size_)) {
if (max_db_size_ < 0)
max_db_size_ = 0;
}
} else if (name_str == "MaxDatabaseAgeInDays") {
if (base::StringToInt(val_str, &max_db_age_)) {
if (max_db_age_ < 0)
max_db_age_ = 0;
}
}
#if defined(OS_WIN)
else if (name_str == "ExternalHandler") {
external_handler_ = sanitizePath(name_str);
} else if (name_str == "AppName") {
app_name_ = sanitizePathComponent(val_str);
}
#endif
} else if (current_section == kCrashKeysSection) {
size_t max_size = 0;
if (val_str == "small")
max_size = crash_keys::kSmallSize;
else if (val_str == "medium")
max_size = crash_keys::kMediumSize;
else if (val_str == "large")
max_size = crash_keys::kLargeSize;
if (max_size == 0)
continue;
crash_keys_.push_back({name_str, max_size});
}
}
fclose(fp);
// Add the list of potential crash keys from chrome, content and other layers.
// Do it here so that they're also exported to the libcef module for Windows.
{
std::vector<base::debug::CrashKey> keys;
crash_keys::GetChromeCrashKeys(keys);
if (!keys.empty()) {
crash_keys_.reserve(crash_keys_.size() + keys.size());
for (const auto& key : keys) {
crash_keys_.push_back({key.key_name, key.max_length});
}
}
}
has_crash_config_file_ = true;
return true;
}
bool CefCrashReporterClient::HasCrashConfigFile() const {
return has_crash_config_file_;
}
#if defined(OS_WIN)
// static
void CefCrashReporterClient::InitializeCrashReportingForProcess() {
if (g_crash_reporter_client)
return;
g_crash_reporter_client = new CefCrashReporterClient();
ANNOTATE_LEAKING_OBJECT_PTR(g_crash_reporter_client);
if (!g_crash_reporter_client->ReadCrashConfigFile())
return;
std::string process_type = install_static::GetSwitchValueFromCommandLine(
::GetCommandLineA(), install_static::kProcessType);
if (process_type != install_static::kCrashpadHandler) {
crash_reporter::SetCrashReporterClient(g_crash_reporter_client);
// If |embedded_handler| is true then we launch another instance of the main
// executable as the crashpad-handler process.
const bool embedded_handler =
!g_crash_reporter_client->HasCrashExternalHandler();
if (embedded_handler) {
crash_reporter::InitializeCrashpadWithEmbeddedHandler(
process_type.empty(), process_type);
} else {
crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
}
}
}
bool CefCrashReporterClient::GetAlternativeCrashDumpLocation(
base::string16* crash_dir) {
// By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
// location to write breakpad crash dumps can be set.
*crash_dir =
install_static::GetEnvironmentString16(L"BREAKPAD_DUMP_LOCATION");
return !crash_dir->empty();
}
void CefCrashReporterClient::GetProductNameAndVersion(
const base::string16& exe_path,
base::string16* product_name,
@ -32,9 +471,27 @@ void CefCrashReporterClient::GetProductNameAndVersion(
*special_build = base::string16();
*channel_name = base::string16();
}
#endif
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
bool CefCrashReporterClient::GetCrashDumpLocation(base::string16* crash_dir) {
// By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
// location to write breakpad crash dumps can be set.
if (GetAlternativeCrashDumpLocation(crash_dir))
return true;
return install_static::GetDefaultCrashDumpLocation(
crash_dir, base::UTF8ToUTF16(app_name_));
}
bool CefCrashReporterClient::GetCrashMetricsLocation(
base::string16* metrics_dir) {
return install_static::GetDefaultUserDataDirectory(
metrics_dir, base::UTF8ToUTF16(app_name_));
}
#elif defined(OS_POSIX)
#if !defined(OS_MACOSX)
void CefCrashReporterClient::GetProductNameAndVersion(
const char** product_name,
const char** version) {
@ -45,27 +502,6 @@ void CefCrashReporterClient::GetProductNameAndVersion(
base::FilePath CefCrashReporterClient::GetReporterLogFilename() {
return base::FilePath(FILE_PATH_LITERAL("uploads.log"));
}
#endif
#if defined(OS_WIN)
bool CefCrashReporterClient::GetCrashDumpLocation(base::string16* crash_dir) {
#else
bool CefCrashReporterClient::GetCrashDumpLocation(base::FilePath* crash_dir) {
#endif
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kCrashDumpsDir))
return false;
base::FilePath crash_directory =
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kCrashDumpsDir);
#if defined(OS_WIN)
*crash_dir = crash_directory.value();
#else
*crash_dir = std::move(crash_directory);
#endif
return true;
}
bool CefCrashReporterClient::EnableBreakpadForProcess(
const std::string& process_type) {
@ -74,3 +510,121 @@ bool CefCrashReporterClient::EnableBreakpadForProcess(
process_type == switches::kZygoteProcess ||
process_type == switches::kGpuProcess;
}
#endif // !defined(OS_MACOSX)
bool CefCrashReporterClient::GetCrashDumpLocation(base::FilePath* crash_dir) {
// By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
// location to write breakpad crash dumps can be set.
std::unique_ptr<base::Environment> env(base::Environment::Create());
std::string alternate_crash_dump_location;
if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) {
base::FilePath crash_dumps_dir_path =
base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location);
PathService::Override(chrome::DIR_CRASH_DUMPS, crash_dumps_dir_path);
}
return PathService::Get(chrome::DIR_CRASH_DUMPS, crash_dir);
}
#endif // !defined(OS_POSIX)
bool CefCrashReporterClient::GetCollectStatsConsent() {
return true;
}
bool CefCrashReporterClient::GetCollectStatsInSample() {
return true;
}
#if defined(OS_WIN) || defined(OS_MACOSX)
bool CefCrashReporterClient::ReportingIsEnforcedByPolicy(
bool* crashpad_enabled) {
*crashpad_enabled = true;
return true;
}
#endif
size_t CefCrashReporterClient::RegisterCrashKeys() {
std::vector<base::debug::CrashKey> keys;
if (!crash_keys_.empty()) {
keys.reserve(crash_keys_.size());
for (const auto& key : crash_keys_) {
keys.push_back({key.key_name_.c_str(), key.max_length_});
}
}
return base::debug::InitCrashKeys(&keys[0], keys.size(),
crash_keys::kChunkMaxLength);
}
#if defined(OS_POSIX) && !defined(OS_MACOSX)
bool CefCrashReporterClient::IsRunningUnattended() {
// Crash upload will only be enabled with Breakpad on Linux if this method
// returns false.
return false;
}
#endif
#if defined(OS_WIN)
size_t CefCrashReporterClient::GetCrashKeyCount() const {
return crash_keys_.size();
}
bool CefCrashReporterClient::GetCrashKey(size_t index,
const char** key_name,
size_t* max_length) const {
if (index >= crash_keys_.size())
return false;
const auto& key = crash_keys_[index];
*key_name = key.key_name_.c_str();
*max_length = key.max_length_;
return true;
}
#endif // defined(OS_WIN)
std::string CefCrashReporterClient::GetCrashServerURL() {
return server_url_;
}
// See HandlerMain() in third_party/crashpad/crashpad/handler/handler_main.cc
// for supported arguments.
void CefCrashReporterClient::GetCrashOptionalArguments(
std::vector<std::string>* arguments) {
if (!rate_limit_)
arguments->push_back(std::string("--no-rate-limit"));
if (max_uploads_ > 0) {
arguments->push_back(
std::string("--max-uploads=") + base::IntToString(max_uploads_));
}
if (max_db_size_ > 0) {
arguments->push_back(
std::string("--max-db-size=") + base::IntToString(max_db_size_));
}
if (max_db_age_ > 0) {
arguments->push_back(
std::string("--max-db-age=") + base::IntToString(max_db_age_));
}
}
#if defined(OS_WIN)
base::string16 CefCrashReporterClient::GetCrashExternalHandler(
const base::string16& exe_dir) {
if (external_handler_.empty())
return CrashReporterClient::GetCrashExternalHandler(exe_dir);
if (isAbsolutePath(external_handler_))
return base::UTF8ToUTF16(external_handler_);
return base::UTF8ToWide(
joinPath(base::UTF16ToUTF8(exe_dir), external_handler_));
}
bool CefCrashReporterClient::HasCrashExternalHandler() const {
return !external_handler_.empty();
}
#endif // defined(OS_WIN)

View File

@ -1,49 +1,109 @@
// 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.
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2016 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 CEF_LIBCEF_COMMON_CRASH_REPORTER_CLIENT_H_
#define CEF_LIBCEF_COMMON_CRASH_REPORTER_CLIENT_H_
#include <vector>
#include <string>
#include "base/macros.h"
#include "build/build_config.h"
#include "components/crash/content/app/crash_reporter_client.h"
// Global object that is instantiated in each process and configures crash
// reporting. On Windows this is created by the
// InitializeCrashReportingForProcess() method called from chrome_elf. On
// Linux and macOS this is created by crash_reporting::BasicStartupComplete().
class CefCrashReporterClient : public crash_reporter::CrashReporterClient {
public:
CefCrashReporterClient();
~CefCrashReporterClient() override;
// Reads the crash config file and returns true on success. Failure to read
// the crash config file will disable crash reporting. This method should be
// called immediately after the CefCrashReporterClient instance is created.
bool ReadCrashConfigFile();
bool HasCrashConfigFile() const;
#if defined(OS_WIN)
// Returns a textual description of the product type and version to include
// in the crash report.
// Called from chrome_elf (chrome_elf/crash/crash_helper.cc) to instantiate
// a process wide instance of CefCrashReporterClient and initialize crash
// reporting for the process. The instance is leaked.
// crash_reporting_win::InitializeCrashReportingForModule() will be called
// later from crash_reporting::PreSandboxStartup() to read global state into
// the module address space.
static void InitializeCrashReportingForProcess();
bool GetAlternativeCrashDumpLocation(base::string16* crash_dir) override;
void GetProductNameAndVersion(const base::string16& exe_path,
base::string16* product_name,
base::string16* version,
base::string16* special_build,
base::string16* channel_name) override;
#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.
bool GetCrashDumpLocation(base::string16* crash_dir) override;
bool GetCrashMetricsLocation(base::string16* metrics_dir) override;
#elif defined(OS_POSIX)
#if !defined(OS_MACOSX)
void GetProductNameAndVersion(const char** product_name,
const char** version) override;
base::FilePath GetReporterLogFilename() override;
#endif
// The location where minidump files should be written. Returns true if
// |crash_dir| was set.
#if defined(OS_WIN)
bool GetCrashDumpLocation(base::string16* crash_dir) override;
#else
bool GetCrashDumpLocation(base::FilePath* crash_dir) override;
#endif
bool EnableBreakpadForProcess(const std::string& process_type) override;
#endif
bool GetCrashDumpLocation(base::FilePath* crash_dir) override;
#endif // defined(OS_POSIX)
// All of these methods must return true to enable crash report upload.
bool GetCollectStatsConsent() override;
bool GetCollectStatsInSample() override;
#if defined(OS_WIN) || defined(OS_MACOSX)
bool ReportingIsEnforcedByPolicy(bool* crashpad_enabled) override;
#endif
size_t RegisterCrashKeys() override;
#if defined(OS_POSIX) && !defined(OS_MACOSX)
bool IsRunningUnattended() override;
#endif
#if defined(OS_WIN)
size_t GetCrashKeyCount() const;
bool GetCrashKey(size_t index,
const char** key_name,
size_t* max_length) const;
#endif
std::string GetCrashServerURL() override;
void GetCrashOptionalArguments(std::vector<std::string>* arguments) override;
#if defined(OS_WIN)
base::string16 GetCrashExternalHandler(
const base::string16& exe_dir) override;
bool HasCrashExternalHandler() const;
#endif
private:
bool has_crash_config_file_ = false;
// Values that will persist until the end of the program.
// Matches the members of base::debug::CrashKey.
struct StoredCrashKey {
std::string key_name_;
size_t max_length_;
};
std::vector<StoredCrashKey> crash_keys_;
std::string server_url_;
bool rate_limit_ = true;
int max_uploads_ = 5;
int max_db_size_ = 20;
int max_db_age_ = 5;
#if defined(OS_WIN)
std::string app_name_ = "CEF";
std::string external_handler_;
#endif
DISALLOW_COPY_AND_ASSIGN(CefCrashReporterClient);
};

View File

@ -0,0 +1,190 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2016 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 "libcef/common/crash_reporting.h"
#include "include/cef_crash_util.h"
#include "libcef/common/cef_switches.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/debug/crash_logging.h"
#include "base/strings/string_util.h"
#include "chrome/common/crash_keys.h"
#include "content/public/common/content_switches.h"
#if defined(OS_MACOSX)
#include "base/mac/foundation_util.h"
#include "components/crash/content/app/crashpad.h"
#include "components/crash/core/common/crash_keys.h"
#include "content/public/common/content_paths.h"
#endif
#if defined(OS_POSIX)
#include "base/lazy_instance.h"
#include "libcef/common/crash_reporter_client.h"
#endif
#if defined(OS_POSIX) && !defined(OS_MACOSX)
#include "components/crash/content/app/breakpad_linux.h"
#endif
#if defined(OS_WIN)
#include "libcef/common/crash_reporting_win.h"
#endif
namespace crash_reporting {
namespace {
bool g_crash_reporting_enabled = false;
#if defined(OS_POSIX)
base::LazyInstance<CefCrashReporterClient>::Leaky g_crash_reporter_client =
LAZY_INSTANCE_INITIALIZER;
void InitCrashReporter(const base::CommandLine& command_line,
const std::string& process_type) {
CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
if (!crash_client->HasCrashConfigFile())
return;
crash_reporter::SetCrashReporterClient(crash_client);
#if defined(OS_MACOSX)
// TODO(mark): Right now, InitializeCrashpad() needs to be called after
// CommandLine::Init() and configuration of chrome::DIR_CRASH_DUMPS. Ideally,
// Crashpad initialization could occur sooner, preferably even before the
// framework dylib is even loaded, to catch potential early crashes.
crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
// Mac Chrome is packaged with a main app bundle and a helper app bundle.
// The main app bundle should only be used for the browser process, so it
// should never see a --type switch (switches::kProcessType). Likewise,
// the helper should always have a --type switch.
//
// This check is done this late so there is already a call to
// base::mac::IsBackgroundOnlyProcess(), so there is no change in
// startup/initialization order.
// The helper's Info.plist marks it as a background only app.
if (base::mac::IsBackgroundOnlyProcess()) {
CHECK(command_line.HasSwitch(switches::kProcessType) &&
!process_type.empty())
<< "Helper application requires --type.";
} else {
CHECK(!command_line.HasSwitch(switches::kProcessType) &&
process_type.empty())
<< "Main application forbids --type, saw " << process_type;
}
g_crash_reporting_enabled = true;
#else // !defined(OS_MACOSX)
breakpad::SetCrashServerURL(crash_client->GetCrashServerURL());
if (process_type != switches::kZygoteProcess) {
// Crash reporting for subprocesses created using the zygote will be
// initialized in ZygoteForked.
breakpad::InitCrashReporter(process_type);
g_crash_reporting_enabled = true;
}
#endif // !defined(OS_MACOSX)
}
#endif // defined(OS_POSIX)
// Used to exclude command-line flags from crash reporting.
bool IsBoringCEFSwitch(const std::string& flag) {
if (crash_keys::IsBoringChromeSwitch(flag))
return true;
static const char* const kIgnoreSwitches[] = {
// CEF internals.
switches::kLogFile,
// Chromium internals.
"content-image-texture-target",
"mojo-platform-channel-handle",
"primordial-pipe-token",
"service-request-channel-token",
};
if (!base::StartsWith(flag, "--", base::CompareCase::SENSITIVE))
return false;
size_t end = flag.find("=");
size_t len = (end == std::string::npos) ? flag.length() - 2 : end - 2;
for (size_t i = 0; i < arraysize(kIgnoreSwitches); ++i) {
if (flag.compare(2, len, kIgnoreSwitches[i]) == 0)
return true;
}
return false;
}
} // namespace
#if defined(OS_POSIX)
// Be aware that logging is not initialized at the time this method is called.
void BasicStartupComplete(base::CommandLine* command_line) {
CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
if (crash_client->ReadCrashConfigFile()) {
#if !defined(OS_MACOSX)
// Breakpad requires this switch.
command_line->AppendSwitch(switches::kEnableCrashReporter);
#endif
}
}
#endif
void PreSandboxStartup(const base::CommandLine& command_line,
const std::string& process_type) {
#if defined(OS_POSIX)
// Initialize crash reporting here on macOS and Linux. Crash reporting on
// Windows is initialized from context.cc.
InitCrashReporter(command_line, process_type);
#elif defined(OS_WIN)
// Initialize crash key globals in the module (libcef) address space.
g_crash_reporting_enabled =
crash_reporting_win::InitializeCrashReportingForModule();
#endif
if (g_crash_reporting_enabled) {
LOG(INFO) << "Crash reporting enabled for process: " <<
(process_type.empty() ? "browser" : process_type.c_str());
}
// After platform crash reporting have been initialized, store the command
// line for crash reporting.
crash_keys::SetSwitchesFromCommandLine(command_line, &IsBoringCEFSwitch);
}
#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
void ZygoteForked(base::CommandLine* command_line,
const std::string& process_type) {
CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
if (crash_client->HasCrashConfigFile()) {
// Breakpad requires this switch.
command_line->AppendSwitch(switches::kEnableCrashReporter);
}
InitCrashReporter(*command_line, process_type);
if (g_crash_reporting_enabled) {
LOG(INFO) << "Crash reporting enabled for process: " << process_type;
}
// Reset the command line for the newly spawned process.
crash_keys::SetSwitchesFromCommandLine(*command_line, &IsBoringCEFSwitch);
}
#endif
} // namespace crash_reporting
bool CefCrashReportingEnabled() {
return crash_reporting::g_crash_reporting_enabled;
}
void CefSetCrashKeyValue(const CefString& key, const CefString& value) {
base::debug::SetCrashKeyValue(key.ToString(), value.ToString());
}

View File

@ -0,0 +1,29 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2016 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 <string>
#include "build/build_config.h"
namespace base {
class CommandLine;
}
namespace crash_reporting {
// Functions are called from similarly named methods in CefMainDelegate.
#if defined(OS_POSIX)
void BasicStartupComplete(base::CommandLine* command_line);
#endif
void PreSandboxStartup(const base::CommandLine& command_line,
const std::string& process_type);
#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
void ZygoteForked(base::CommandLine* command_line,
const std::string& process_type);
#endif
} // namespace crash_reporting

View File

@ -0,0 +1,120 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2016 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 <windows.h>
#include "libcef/common/crash_reporting_win.h"
#include "base/debug/crash_logging.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/chrome_constants.h"
#include "components/crash/core/common/crash_keys.h"
namespace crash_reporting_win {
namespace {
// exported in crash_reporter_client.cc:
// size_t __declspec(dllexport) __cdecl GetCrashKeyCountImpl.
typedef size_t(__cdecl* GetCrashKeyCount)();
// exported in crash_reporter_client.cc:
// bool __declspec(dllexport) __cdecl GetCrashKeyImpl.
typedef bool(__cdecl* GetCrashKey)(size_t, const char**, size_t*);
size_t GetCrashKeyCountTrampoline() {
static GetCrashKeyCount get_crash_key_count = []() {
HMODULE elf_module = GetModuleHandle(chrome::kChromeElfDllName);
return reinterpret_cast<GetCrashKeyCount>(
elf_module ? GetProcAddress(elf_module, "GetCrashKeyCountImpl")
: nullptr);
}();
if (get_crash_key_count) {
return (get_crash_key_count)();
}
return 0;
}
bool GetCrashKeyTrampoline(size_t index,
const char** key_name,
size_t* max_length) {
static GetCrashKey get_crash_key = []() {
HMODULE elf_module = GetModuleHandle(chrome::kChromeElfDllName);
return reinterpret_cast<GetCrashKey>(
elf_module ? GetProcAddress(elf_module, "GetCrashKeyImpl")
: nullptr);
}();
if (get_crash_key) {
return (get_crash_key)(index, key_name, max_length);
}
return false;
}
// From chrome/common/child_process_logging_win.cc:
// exported in breakpad_win.cc/crashpad_win.cc:
// void __declspec(dllexport) __cdecl SetCrashKeyValueImpl.
typedef void(__cdecl* SetCrashKeyValue)(const wchar_t*, const wchar_t*);
// exported in breakpad_win.cc/crashpad_win.cc:
// void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl.
typedef void(__cdecl* ClearCrashKeyValue)(const wchar_t*);
void SetCrashKeyValueTrampoline(const base::StringPiece& key,
const base::StringPiece& value) {
static SetCrashKeyValue set_crash_key = []() {
HMODULE elf_module = GetModuleHandle(chrome::kChromeElfDllName);
return reinterpret_cast<SetCrashKeyValue>(
elf_module ? GetProcAddress(elf_module, "SetCrashKeyValueImpl")
: nullptr);
}();
if (set_crash_key) {
(set_crash_key)(base::UTF8ToWide(key).data(),
base::UTF8ToWide(value).data());
}
}
void ClearCrashKeyValueTrampoline(const base::StringPiece& key) {
static ClearCrashKeyValue clear_crash_key = []() {
HMODULE elf_module = GetModuleHandle(chrome::kChromeElfDllName);
return reinterpret_cast<ClearCrashKeyValue>(
elf_module ? GetProcAddress(elf_module, "ClearCrashKeyValueImpl")
: nullptr);
}();
if (clear_crash_key)
(clear_crash_key)(base::UTF8ToWide(key).data());
}
} // namespace
bool InitializeCrashReportingForModule() {
base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueTrampoline,
&ClearCrashKeyValueTrampoline);
std::vector<base::debug::CrashKey> keys;
size_t key_ct = GetCrashKeyCountTrampoline();
if (key_ct > 0U) {
keys.reserve(key_ct);
const char* key_name;
size_t max_length;
for (size_t i = 0; i < key_ct; ++i) {
if (GetCrashKeyTrampoline(i, &key_name, &max_length))
keys.push_back({key_name, max_length});
}
}
if (!keys.empty()) {
base::debug::InitCrashKeys(&keys[0], keys.size(),
crash_keys::kChunkMaxLength);
return true;
}
return false;
}
} // namespace crash_reporting_win

View File

@ -0,0 +1,11 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2016 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.
namespace crash_reporting_win {
// Called from libcef to initialize crash key globals. Retrieves the necessary
// state from chrome_elf via exported functions.
bool InitializeCrashReportingForModule();
} // namespace crash_reporting_win

View File

@ -7,7 +7,7 @@
#include "libcef/browser/context.h"
#include "libcef/common/cef_switches.h"
#include "libcef/common/command_line_impl.h"
#include "libcef/common/crash_reporter_client.h"
#include "libcef/common/crash_reporting.h"
#include "libcef/common/extensions/extensions_util.h"
#include "libcef/renderer/content_renderer_client.h"
#include "libcef/utility/content_utility_client.h"
@ -16,7 +16,6 @@
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@ -57,18 +56,11 @@
#if defined(OS_MACOSX)
#include "libcef/common/util_mac.h"
#include "base/mac/os_crash_dumps.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
#include "components/crash/content/app/crashpad.h"
#include "components/crash/core/common/crash_keys.h"
#include "content/public/common/content_paths.h"
#endif
#if defined(OS_POSIX) && !defined(OS_MACOSX)
#include "components/crash/content/app/breakpad_linux.h"
#endif
#if defined(OS_LINUX)
#include "base/environment.h"
#include "base/nix/xdg_util.h"
@ -76,11 +68,6 @@
namespace {
#if defined(OS_POSIX)
base::LazyInstance<CefCrashReporterClient>::Leaky g_crash_reporter_client =
LAZY_INSTANCE_INITIALIZER;
#endif
#if defined(OS_MACOSX)
base::FilePath GetFrameworksPath() {
@ -327,6 +314,12 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) {
std::string process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);
#if defined(OS_POSIX)
// Read the crash configuration file. Platforms using Breakpad also add a
// command-line switch. On Windows this is done from chrome_elf.
crash_reporting::BasicStartupComplete(command_line);
#endif
if (process_type.empty()) {
// In the browser process. Populate the global command-line object.
const CefSettings& settings = CefContext::Get()->settings();
@ -524,18 +517,6 @@ void CefMainDelegate::PreSandboxStartup() {
const std::string& process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);
#if defined(OS_POSIX)
if (command_line->HasSwitch(switches::kEnableCrashReporter)) {
crash_reporter::SetCrashReporterClient(g_crash_reporter_client.Pointer());
#if defined(OS_MACOSX)
InitMacCrashReporter(*command_line, process_type);
#else
if (process_type != switches::kZygoteProcess)
breakpad::InitCrashReporter(process_type);
#endif
}
#endif // defined(OS_POSIX)
if (process_type.empty()) {
// Only override these paths when executing the main process.
#if defined(OS_MACOSX)
@ -544,9 +525,13 @@ void CefMainDelegate::PreSandboxStartup() {
OverridePepperFlashSystemPluginPath();
// Paths used to locate spell checking dictionary files.
const base::FilePath& user_data_path = GetUserDataPath();
PathService::Override(chrome::DIR_USER_DATA, user_data_path);
// Path used for crash dumps.
PathService::Override(chrome::DIR_CRASH_DUMPS, user_data_path);
// Path used for spell checking dictionary files.
PathService::OverrideAndCreateIfNeeded(
chrome::DIR_APP_DICTIONARIES,
user_data_path.AppendASCII("Dictionaries"),
@ -557,6 +542,10 @@ void CefMainDelegate::PreSandboxStartup() {
if (command_line->HasSwitch(switches::kDisablePackLoading))
content_client_.set_pack_loading_disabled(true);
// Initialize crash reporting state for this process/module.
// chrome::DIR_CRASH_DUMPS must be configured before calling this function.
crash_reporting::PreSandboxStartup(*command_line, process_type);
InitializeResourceBundle();
chrome::InitializePDF();
}
@ -609,13 +598,12 @@ void CefMainDelegate::ProcessExiting(const std::string& process_type) {
#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
void CefMainDelegate::ZygoteForked() {
const base::CommandLine* command_line =
base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kEnableCrashReporter)) {
const std::string& process_type = command_line->GetSwitchValueASCII(
const std::string& process_type = command_line->GetSwitchValueASCII(
switches::kProcessType);
breakpad::InitCrashReporter(process_type);
}
// Initialize crash reporting state for the newly forked process.
crash_reporting::ZygoteForked(command_line, process_type);
}
#endif
@ -757,51 +745,3 @@ void CefMainDelegate::InitializeResourceBundle() {
content_client_.set_allow_pack_file_load(false);
}
}
#if defined(OS_MACOSX)
// Based on ChromeMainDelegate::InitMacCrashReporter.
void CefMainDelegate::InitMacCrashReporter(
const base::CommandLine& command_line,
const std::string& process_type) {
// TODO(mark): Right now, InitializeCrashpad() needs to be called after
// CommandLine::Init() and chrome::RegisterPathProvider(). Ideally, Crashpad
// initialization could occur sooner, preferably even before the framework
// dylib is even loaded, to catch potential early crashes.
const bool browser_process = process_type.empty();
const bool install_from_dmg_relauncher_process =
process_type == switches::kRelauncherProcess &&
command_line.HasSwitch(switches::kRelauncherProcessDMGDevice);
const bool initial_client =
browser_process || install_from_dmg_relauncher_process;
crash_reporter::InitializeCrashpad(initial_client, process_type);
if (!browser_process) {
std::string metrics_client_id =
command_line.GetSwitchValueASCII(switches::kMetricsClientID);
crash_keys::SetMetricsClientIdFromGUID(metrics_client_id);
}
// Mac Chrome is packaged with a main app bundle and a helper app bundle.
// The main app bundle should only be used for the browser process, so it
// should never see a --type switch (switches::kProcessType). Likewise,
// the helper should always have a --type switch.
//
// This check is done this late so there is already a call to
// base::mac::IsBackgroundOnlyProcess(), so there is no change in
// startup/initialization order.
// The helper's Info.plist marks it as a background only app.
if (base::mac::IsBackgroundOnlyProcess()) {
CHECK(command_line.HasSwitch(switches::kProcessType) &&
!process_type.empty())
<< "Helper application requires --type.";
} else {
CHECK(!command_line.HasSwitch(switches::kProcessType) &&
process_type.empty())
<< "Main application forbids --type, saw " << process_type;
}
}
#endif // defined(OS_MACOSX)

View File

@ -56,11 +56,6 @@ class CefMainDelegate : public content::ContentMainDelegate {
private:
void InitializeResourceBundle();
#if defined(OS_MACOSX)
void InitMacCrashReporter(const base::CommandLine& command_line,
const std::string& process_type);
#endif // defined(OS_MACOSX)
std::unique_ptr<content::BrowserMainRunner> browser_runner_;
std::unique_ptr<base::Thread> ui_thread_;

19
libcef/features/BUILD.gn Normal file
View File

@ -0,0 +1,19 @@
# Copyright 2016 The Chromium Embedded Framework 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("//build/buildflag_header.gni")
import("//cef/libcef/features/features.gni")
# This file is in a separate directory so all targets in the build can refer to
# the buildflag header to get the necessary preprocessor defines without
# bringing in any CEF targets. Other targets can depend on this target
# regardless of whether CEF is being built.
buildflag_header("features") {
header = "features.h"
flags = [
"ENABLE_CEF=$enable_cef",
]
}

View File

@ -0,0 +1,7 @@
# Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
declare_args() {
enable_cef = true
}

View File

@ -12,6 +12,8 @@
#include "include/cef_app.h"
#include "include/capi/cef_app_capi.h"
#include "include/cef_crash_util.h"
#include "include/capi/cef_crash_util_capi.h"
#include "include/cef_file_util.h"
#include "include/capi/cef_file_util_capi.h"
#include "include/cef_geolocation.h"
@ -391,6 +393,35 @@ CEF_EXPORT void cef_enable_highdpi_support() {
CefEnableHighDPISupport();
}
CEF_EXPORT int cef_crash_reporting_enabled() {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
bool _retval = CefCrashReportingEnabled();
// Return type: bool
return _retval;
}
CEF_EXPORT void cef_set_crash_key_value(const cef_string_t* key,
const cef_string_t* value) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: key; type: string_byref_const
DCHECK(key);
if (!key)
return;
// Verify param: value; type: string_byref_const
DCHECK(value);
if (!value)
return;
// Execute
CefSetCrashKeyValue(
CefString(key),
CefString(value));
}
CEF_EXPORT int cef_create_directory(const cef_string_t* full_path) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING

View File

@ -12,6 +12,8 @@
#include "include/cef_app.h"
#include "include/capi/cef_app_capi.h"
#include "include/cef_crash_util.h"
#include "include/capi/cef_crash_util_capi.h"
#include "include/cef_file_util.h"
#include "include/capi/cef_file_util_capi.h"
#include "include/cef_geolocation.h"
@ -383,6 +385,35 @@ CEF_GLOBAL void CefEnableHighDPISupport() {
cef_enable_highdpi_support();
}
CEF_GLOBAL bool CefCrashReportingEnabled() {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
int _retval = cef_crash_reporting_enabled();
// Return type: bool
return _retval?true:false;
}
CEF_GLOBAL void CefSetCrashKeyValue(const CefString& key,
const CefString& value) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: key; type: string_byref_const
DCHECK(!key.empty());
if (key.empty())
return;
// Verify param: value; type: string_byref_const
DCHECK(!value.empty());
if (value.empty())
return;
// Execute
cef_set_crash_key_value(
key.GetStruct(),
value.GetStruct());
}
CEF_GLOBAL bool CefCreateDirectory(const CefString& full_path) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING

View File

@ -294,4 +294,19 @@ patches = [
'name': 'render_widget_latency_2060',
'path': '../',
},
{
# Implement breakpad/crashpad customization required by CEF.
# https://bitbucket.org/chromiumembedded/cef/issues/1995
'name': 'crashpad_1995',
'path': '../',
},
{
# Support customization of crash report pruning limits.
# https://bugs.chromium.org/p/crashpad/issues/detail?id=142
#
# Implement better rate-limiting/retry logic.
# https://bugs.chromium.org/p/crashpad/issues/detail?id=23
'name': 'crashpad_tp_1995',
'path': '../third_party/crashpad/',
},
]

View File

@ -0,0 +1,509 @@
diff --git build/secondary/third_party/crashpad/crashpad/handler/BUILD.gn build/secondary/third_party/crashpad/crashpad/handler/BUILD.gn
index 6cd8b9f..dfbbdc6 100644
--- build/secondary/third_party/crashpad/crashpad/handler/BUILD.gn
+++ build/secondary/third_party/crashpad/crashpad/handler/BUILD.gn
@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//cef/libcef/features/features.gni")
+
static_library("handler_lib") {
sources = [
"crash_report_upload_thread.cc",
@@ -26,8 +28,18 @@ static_library("handler_lib") {
"../snapshot",
"../tools:tool_support",
"//base",
+ "//cef/libcef/features",
]
+ if (enable_cef) {
+ sources += [
+ "//cef/libcef/common/cef_crash_report_upload_thread.cc",
+ "//cef/libcef/common/cef_crash_report_upload_thread.h",
+ ]
+
+ include_dirs += [ "//cef" ]
+ }
+
if (is_win) {
cflags = [ "/wd4201" ]
}
diff --git chrome/common/crash_keys.cc chrome/common/crash_keys.cc
index 800f704..e4b4063 100644
--- chrome/common/crash_keys.cc
+++ chrome/common/crash_keys.cc
@@ -4,6 +4,8 @@
#include "chrome/common/crash_keys.h"
+#include <iterator>
+
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/format_macros.h"
@@ -94,7 +96,7 @@ const char kViewCount[] = "view-count";
const char kZeroEncodeDetails[] = "zero-encode-details";
-size_t RegisterChromeCrashKeys() {
+void GetChromeCrashKeys(std::vector<base::debug::CrashKey>& keys) {
// The following keys may be chunked by the underlying crash logging system,
// but ultimately constitute a single key-value pair.
//
@@ -250,10 +252,16 @@ size_t RegisterChromeCrashKeys() {
// This dynamic set of keys is used for sets of key value pairs when gathering
// a collection of data, like command line switches or extension IDs.
- std::vector<base::debug::CrashKey> keys(
- fixed_keys, fixed_keys + arraysize(fixed_keys));
+ keys.reserve(keys.size() + arraysize(fixed_keys));
+ std::copy(fixed_keys, fixed_keys + arraysize(fixed_keys),
+ std::back_inserter(keys));
crash_keys::GetCrashKeysForCommandLineSwitches(&keys);
+}
+
+size_t RegisterChromeCrashKeys() {
+ std::vector<base::debug::CrashKey> keys;
+ GetChromeCrashKeys(keys);
// Register the extension IDs.
{
@@ -287,7 +295,7 @@ size_t RegisterChromeCrashKeys() {
return base::debug::InitCrashKeys(&keys.at(0), keys.size(), kChunkMaxLength);
}
-static bool IsBoringSwitch(const std::string& flag) {
+bool IsBoringChromeSwitch(const std::string& flag) {
static const char* const kIgnoreSwitches[] = {
switches::kEnableLogging,
switches::kFlagSwitchesBegin,
@@ -343,7 +351,7 @@ static bool IsBoringSwitch(const std::string& flag) {
}
void SetCrashKeysFromCommandLine(const base::CommandLine& command_line) {
- return SetSwitchesFromCommandLine(command_line, &IsBoringSwitch);
+ return SetSwitchesFromCommandLine(command_line, &IsBoringChromeSwitch);
}
void SetActiveExtensions(const std::set<std::string>& extensions) {
diff --git chrome/common/crash_keys.h chrome/common/crash_keys.h
index 6f66031..1abcdf8 100644
--- chrome/common/crash_keys.h
+++ chrome/common/crash_keys.h
@@ -23,10 +23,18 @@ class CommandLine;
namespace crash_keys {
+// Returns the list of potential crash keys that can be sent to the crash
+// server.
+void GetChromeCrashKeys(std::vector<base::debug::CrashKey>& keys);
+
// Registers all of the potential crash keys that can be sent to the crash
// reporting server. Returns the size of the union of all keys.
size_t RegisterChromeCrashKeys();
+// Returns true if the specified command-line flag should be excluded from
+// crash reporting.
+bool IsBoringChromeSwitch(const std::string& flag);
+
// Sets the kNumSwitches key and the set of keys named using kSwitchFormat based
// on the given |command_line|.
void SetCrashKeysFromCommandLine(const base::CommandLine& command_line);
diff --git chrome/install_static/install_util.cc chrome/install_static/install_util.cc
index edec76d..1db1c9c 100644
--- chrome/install_static/install_util.cc
+++ chrome/install_static/install_util.cc
@@ -473,7 +473,9 @@ bool IsNonBrowserProcess() {
return g_process_type == ProcessType::NON_BROWSER_PROCESS;
}
-bool GetDefaultUserDataDirectory(std::wstring* result) {
+bool GetDefaultUserDataDirectory(
+ std::wstring* result,
+ const std::wstring& install_sub_directory) {
// This environment variable should be set on Windows Vista and later
// (https://msdn.microsoft.com/library/windows/desktop/dd378457.aspx).
std::wstring user_data_dir = GetEnvironmentString16(L"LOCALAPPDATA");
@@ -493,17 +495,23 @@ bool GetDefaultUserDataDirectory(std::wstring* result) {
result->swap(user_data_dir);
if ((*result)[result->length() - 1] != L'\\')
result->push_back(L'\\');
- AppendChromeInstallSubDirectory(result, true /* include_suffix */);
+ if (!install_sub_directory.empty()) {
+ result->append(install_sub_directory);
+ } else {
+ AppendChromeInstallSubDirectory(result, true /* include_suffix */);
+ }
result->push_back(L'\\');
result->append(kUserDataDirname);
return true;
}
-bool GetDefaultCrashDumpLocation(std::wstring* crash_dir) {
+bool GetDefaultCrashDumpLocation(
+ std::wstring* crash_dir,
+ const std::wstring& install_sub_directory) {
// In order to be able to start crash handling very early, we do not rely on
// chrome's PathService entries (for DIR_CRASH_DUMPS) being available on
// Windows. See https://crbug.com/564398.
- if (!GetDefaultUserDataDirectory(crash_dir))
+ if (!GetDefaultUserDataDirectory(crash_dir, install_sub_directory))
return false;
// We have to make sure the user data dir exists on first run. See
diff --git chrome/install_static/install_util.h chrome/install_static/install_util.h
index 4ded522..81eba43 100644
--- chrome/install_static/install_util.h
+++ chrome/install_static/install_util.h
@@ -86,14 +86,18 @@ bool IsNonBrowserProcess();
// TODO(ananta)
// http://crbug.com/604923
// Unify this with the Browser Distribution code.
-bool GetDefaultUserDataDirectory(std::wstring* result);
+bool GetDefaultUserDataDirectory(
+ std::wstring* result,
+ const std::wstring& install_sub_directory = std::wstring());
// Populates |crash_dir| with the default crash dump location regardless of
// whether DIR_USER_DATA or DIR_CRASH_DUMPS has been overridden.
// TODO(ananta)
// http://crbug.com/604923
// Unify this with the Browser Distribution code.
-bool GetDefaultCrashDumpLocation(std::wstring* crash_dir);
+bool GetDefaultCrashDumpLocation(
+ std::wstring* crash_dir,
+ const std::wstring& install_sub_directory = std::wstring());
// Returns the contents of the specified |variable_name| from the environment
// block of the calling process. Returns an empty string if the variable does
diff --git chrome_elf/BUILD.gn chrome_elf/BUILD.gn
index 0629b6f..9c150b5 100644
--- chrome_elf/BUILD.gn
+++ chrome_elf/BUILD.gn
@@ -7,6 +7,7 @@
import("//build/config/compiler/compiler.gni")
import("//build/config/win/manifest.gni")
+import("//cef/libcef/features/features.gni")
import("//chrome/process_version_rc_template.gni")
import("//testing/test.gni")
@@ -138,16 +139,40 @@ static_library("blacklist") {
static_library("crash") {
sources = [
- "../chrome/app/chrome_crash_reporter_client_win.cc",
- "../chrome/app/chrome_crash_reporter_client_win.h",
- "../chrome/common/chrome_result_codes.h",
"crash/crash_helper.cc",
"crash/crash_helper.h",
]
+
+ if (enable_cef) {
+ sources += [
+ "//cef/libcef/common/crash_reporter_client.cc",
+ "//cef/libcef/common/crash_reporter_client.h",
+
+ # Required for crash_keys::GetChromeCrashKeys.
+ # Otherwise we need to copy this array into CEF, which would be difficult
+ # to maintain.
+ "//chrome/common/crash_keys.cc",
+ "//chrome/common/chrome_switches.cc",
+ "//components/flags_ui/flags_ui_switches.cc",
+ "//content/public/common/content_switches.cc",
+
+ ]
+ include_dirs = [
+ "//cef",
+ ]
+ } else {
+ sources += [
+ "//chrome/app/chrome_crash_reporter_client_win.cc",
+ "//chrome/app/chrome_crash_reporter_client_win.h",
+ "//chrome/common/chrome_result_codes.h",
+ ]
+ }
+
deps = [
":hook_util",
"//base:base", # This needs to go. DEP of app, crash_keys, client.
"//base:base_static", # pe_image
+ "//cef/libcef/features",
"//chrome/install_static:install_static_util",
"//components/crash/content/app:app",
"//components/crash/core/common", # crash_keys
diff --git chrome_elf/crash/crash_helper.cc chrome_elf/crash/crash_helper.cc
index c658fa9..8c4a145 100644
--- chrome_elf/crash/crash_helper.cc
+++ chrome_elf/crash/crash_helper.cc
@@ -11,12 +11,17 @@
#include <string>
#include <vector>
+#include "cef/libcef/features/features.h"
#include "chrome/app/chrome_crash_reporter_client_win.h"
#include "chrome_elf/hook_util/hook_util.h"
#include "components/crash/content/app/crashpad.h"
#include "components/crash/core/common/crash_keys.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#if BUILDFLAG(ENABLE_CEF)
+#include "cef/libcef/common/crash_reporter_client.h"
+#endif
+
namespace {
// Crash handling from elf is only enabled for the chrome.exe process.
@@ -74,7 +79,11 @@ bool InitializeCrashReporting() {
g_crash_reports = new std::vector<crash_reporter::Report>;
g_set_unhandled_exception_filter = new elf_hook::IATHook();
+#if BUILDFLAG(ENABLE_CEF)
+ CefCrashReporterClient::InitializeCrashReportingForProcess();
+#else
ChromeCrashReporterClient::InitializeCrashReportingForProcess();
+#endif
g_crash_helper_enabled = true;
return true;
diff --git components/crash/content/app/breakpad_linux.cc components/crash/content/app/breakpad_linux.cc
index 9ebc33f..c013b36 100644
--- components/crash/content/app/breakpad_linux.cc
+++ components/crash/content/app/breakpad_linux.cc
@@ -29,6 +29,7 @@
#include "base/command_line.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
+#include "base/debug/leak_annotations.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/linux_util.h"
@@ -89,6 +90,7 @@ namespace {
#if !defined(OS_CHROMEOS)
const char kUploadURL[] = "https://clients2.google.com/cr/report";
+const char* g_crash_server_url = kUploadURL;
#endif
bool g_is_crash_reporter_enabled = false;
@@ -654,7 +656,7 @@ bool CrashDone(const MinidumpDescriptor& minidump,
info.process_type_length = 7;
info.distro = base::g_linux_distro;
info.distro_length = my_strlen(base::g_linux_distro);
- info.upload = upload;
+ info.upload = upload && g_crash_server_url;
info.process_start_time = g_process_start_time;
info.oom_size = base::g_oom_size;
info.pid = g_pid;
@@ -1275,7 +1277,7 @@ void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
header_content_encoding,
header_content_type,
post_file,
- kUploadURL,
+ g_crash_server_url,
"--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
@@ -1880,6 +1882,17 @@ void InitCrashReporter(const std::string& process_type) {
PostEnableBreakpadInitialization();
}
+void SetCrashServerURL(const std::string& url) {
+ if (url.empty()) {
+ g_crash_server_url = nullptr;
+ } else {
+ char* new_url = new char[url.size() + 1];
+ ANNOTATE_LEAKING_OBJECT_PTR(new_url);
+ strcpy(new_url, url.c_str());
+ g_crash_server_url = new_url;
+ }
+}
+
#if defined(OS_ANDROID)
void InitNonBrowserCrashReporterForAndroid(const std::string& process_type) {
const base::CommandLine* command_line =
diff --git components/crash/content/app/breakpad_linux.h components/crash/content/app/breakpad_linux.h
index 3316fa0..df90dbd 100644
--- components/crash/content/app/breakpad_linux.h
+++ components/crash/content/app/breakpad_linux.h
@@ -16,6 +16,9 @@ namespace breakpad {
// Turns on the crash reporter in any process.
extern void InitCrashReporter(const std::string& process_type);
+// Set the crash server URL.
+void SetCrashServerURL(const std::string& url);
+
#if defined(OS_ANDROID)
const char kWebViewSingleProcessType[] = "webview";
diff --git components/crash/content/app/crash_reporter_client.cc components/crash/content/app/crash_reporter_client.cc
index 3dfbd99..cb99c1e 100644
--- components/crash/content/app/crash_reporter_client.cc
+++ components/crash/content/app/crash_reporter_client.cc
@@ -141,6 +141,26 @@ bool CrashReporterClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) {
}
#endif
+bool CrashReporterClient::EnableBreakpadForProcess(
+ const std::string& process_type) {
+ return false;
+}
+
+std::string CrashReporterClient::GetCrashServerURL() {
+ return std::string();
+}
+
+void CrashReporterClient::GetCrashOptionalArguments(
+ std::vector<std::string>* arguments) {
+}
+
+#if defined(OS_WIN)
+base::string16 CrashReporterClient::GetCrashExternalHandler(
+ const base::string16& exe_dir) {
+ return exe_dir + L"\\crashpad_handler.exe";
+}
+#endif
+
#if defined(OS_ANDROID)
int CrashReporterClient::GetAndroidMinidumpDescriptor() {
return 0;
@@ -165,9 +185,4 @@ bool CrashReporterClient::ShouldEnableBreakpadMicrodumps() {
}
#endif
-bool CrashReporterClient::EnableBreakpadForProcess(
- const std::string& process_type) {
- return false;
-}
-
} // namespace crash_reporter
diff --git components/crash/content/app/crash_reporter_client.h components/crash/content/app/crash_reporter_client.h
index 25ae505..349ee49 100644
--- components/crash/content/app/crash_reporter_client.h
+++ components/crash/content/app/crash_reporter_client.h
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <string>
+#include <vector>
#include "base/strings/string16.h"
#include "build/build_config.h"
@@ -176,6 +177,17 @@ class CrashReporterClient {
// Returns true if breakpad should run in the given process type.
virtual bool EnableBreakpadForProcess(const std::string& process_type);
+
+ // Returns the URL for submitting crash reports.
+ virtual std::string GetCrashServerURL();
+
+ // Populate |arguments| with additional optional arguments.
+ virtual void GetCrashOptionalArguments(std::vector<std::string>* arguments);
+
+#if defined(OS_WIN)
+ // Returns the absolute path to the external crash handler exe.
+ virtual base::string16 GetCrashExternalHandler(const base::string16& exe_dir);
+#endif
};
} // namespace crash_reporter
diff --git components/crash/content/app/crashpad_mac.mm components/crash/content/app/crashpad_mac.mm
index 7df66ea..f841aea 100644
--- components/crash/content/app/crashpad_mac.mm
+++ components/crash/content/app/crashpad_mac.mm
@@ -16,11 +16,14 @@
#include "base/logging.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
+#include "base/path_service.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 "components/crash/content/app/crash_switches.h"
+#include "content/public/common/content_paths.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"
@@ -40,9 +43,10 @@
if (initial_client) {
@autoreleasepool {
- base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath();
- base::FilePath handler_path =
- framework_bundle_path.Append("Helpers").Append("crashpad_handler");
+ // Use the same subprocess helper exe.
+ base::FilePath handler_path;
+ PathService::Get(content::CHILD_PROCESS_EXE, &handler_path);
+ DCHECK(!handler_path.empty());
// Is there a way to recover if this fails?
CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
@@ -54,7 +58,7 @@
// crash server won't have symbols for any other build types.
std::string url = "https://clients2.google.com/cr/report";
#else
- std::string url;
+ std::string url = crash_reporter_client->GetCrashServerURL();
#endif
std::map<std::string, std::string> process_annotations;
@@ -90,6 +94,12 @@
"--reset-own-crash-exception-port-to-system-default");
}
+ // Since we're using the same subprocess helper exe we must specify the
+ // process type.
+ arguments.push_back(std::string("--type=") + switches::kCrashpadHandler);
+
+ crash_reporter_client->GetCrashOptionalArguments(&arguments);
+
crashpad::CrashpadClient crashpad_client;
bool result = crashpad_client.StartHandler(handler_path,
database_path,
diff --git components/crash/content/app/crashpad_win.cc components/crash/content/app/crashpad_win.cc
index a22af31..bc5086e 100644
--- components/crash/content/app/crashpad_win.cc
+++ components/crash/content/app/crashpad_win.cc
@@ -81,7 +81,7 @@ base::FilePath PlatformCrashpadInitialization(bool initial_client,
#if defined(GOOGLE_CHROME_BUILD)
std::string url = "https://clients2.google.com/cr/report";
#else
- std::string url;
+ std::string url = crash_reporter_client->GetCrashServerURL();
#endif
// Allow the crash server to be overridden for testing. If the variable
@@ -115,9 +115,12 @@ base::FilePath PlatformCrashpadInitialization(bool initial_client,
arguments.push_back("/prefetch:7");
} else {
base::FilePath exe_dir = exe_file.DirName();
- exe_file = exe_dir.Append(FILE_PATH_LITERAL("crashpad_handler.exe"));
+ exe_file = base::FilePath(
+ crash_reporter_client->GetCrashExternalHandler(exe_dir.value()));
}
+ crash_reporter_client->GetCrashOptionalArguments(&arguments);
+
g_crashpad_client.Get().StartHandler(
exe_file, database_path, metrics_path, url, process_annotations,
arguments, false, false);
diff --git content/browser/frame_host/debug_urls.cc content/browser/frame_host/debug_urls.cc
index 2e61cc1..6b8b943 100644
--- content/browser/frame_host/debug_urls.cc
+++ content/browser/frame_host/debug_urls.cc
@@ -189,7 +189,9 @@ bool HandleDebugURL(const GURL& url, ui::PageTransition transition) {
cc::switches::kEnableGpuBenchmarking) &&
(PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED));
- if (!(transition & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR) &&
+ // CEF does not use PAGE_TRANSITION_FROM_ADDRESS_BAR.
+ if (!(transition & (ui::PAGE_TRANSITION_TYPED ||
+ ui::PAGE_TRANSITION_FROM_ADDRESS_BAR)) &&
!is_telemetry_navigation)
return false;

View File

@ -0,0 +1,290 @@
diff --git crashpad/client/prune_crash_reports.cc crashpad/client/prune_crash_reports.cc
index 3aaaeee..d99fcb4 100644
--- crashpad/client/prune_crash_reports.cc
+++ crashpad/client/prune_crash_reports.cc
@@ -67,13 +67,19 @@ void PruneCrashReportDatabase(CrashReportDatabase* database,
}
// static
-std::unique_ptr<PruneCondition> PruneCondition::GetDefault() {
+std::unique_ptr<PruneCondition> PruneCondition::GetDefault(
+ int max_size_in_mb,
+ int max_age_in_days) {
// DatabaseSizePruneCondition must be the LHS so that it is always evaluated,
// due to the short-circuting behavior of BinaryPruneCondition.
+ if (max_size_in_mb <= 0)
+ max_size_in_mb = 128;
+ if (max_age_in_days <= 0)
+ max_age_in_days = 365;
return base::WrapUnique(
new BinaryPruneCondition(BinaryPruneCondition::OR,
- new DatabaseSizePruneCondition(1024 * 128),
- new AgePruneCondition(365)));
+ new DatabaseSizePruneCondition(max_size_in_mb),
+ new AgePruneCondition(max_age_in_days)));
}
static const time_t kSecondsInDay = 60 * 60 * 24;
diff --git crashpad/client/prune_crash_reports.h crashpad/client/prune_crash_reports.h
index b66e9349..86d1f15 100644
--- crashpad/client/prune_crash_reports.h
+++ crashpad/client/prune_crash_reports.h
@@ -57,7 +57,8 @@ class PruneCondition {
//! of 128 MB.
//!
//! \return A PruneCondition for use with PruneCrashReportDatabase().
- static std::unique_ptr<PruneCondition> GetDefault();
+ static std::unique_ptr<PruneCondition> GetDefault(int max_size_in_mb,
+ int max_age_in_days);
virtual ~PruneCondition() {}
diff --git crashpad/client/settings.cc crashpad/client/settings.cc
index d018e37f..47d8110 100644
--- crashpad/client/settings.cc
+++ crashpad/client/settings.cc
@@ -38,7 +38,7 @@ void ScopedLockedFileHandleTraits::Free(FileHandle handle) {
struct Settings::Data {
static const uint32_t kSettingsMagic = 'CPds';
- static const uint32_t kSettingsVersion = 1;
+ static const uint32_t kSettingsVersion = 2;
enum Options : uint32_t {
kUploadsEnabled = 1 << 0,
@@ -49,6 +49,9 @@ struct Settings::Data {
options(0),
padding_0(0),
last_upload_attempt_time(0),
+ next_upload_attempt_time(0),
+ backoff_step(0),
+ padding_1(0),
client_id() {}
uint32_t magic;
@@ -56,6 +59,9 @@ struct Settings::Data {
uint32_t options;
uint32_t padding_0;
uint64_t last_upload_attempt_time; // time_t
+ uint64_t next_upload_attempt_time; // time_t
+ uint32_t backoff_step;
+ uint32_t padding_1;
UUID client_id;
};
@@ -141,6 +147,56 @@ bool Settings::SetLastUploadAttemptTime(time_t time) {
return WriteSettings(handle.get(), settings);
}
+bool Settings::GetNextUploadAttemptTime(time_t* time) {
+ DCHECK(initialized_.is_valid());
+
+ Data settings;
+ if (!OpenAndReadSettings(&settings))
+ return false;
+
+ *time = InRangeCast<time_t>(settings.next_upload_attempt_time,
+ std::numeric_limits<time_t>::max());
+ return true;
+}
+
+bool Settings::SetNextUploadAttemptTime(time_t time) {
+ DCHECK(initialized_.is_valid());
+
+ Data settings;
+ ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
+ if (!handle.is_valid())
+ return false;
+
+ settings.next_upload_attempt_time = InRangeCast<uint64_t>(time, 0);
+
+ return WriteSettings(handle.get(), settings);
+}
+
+bool Settings::GetBackoffStep(int* step) {
+ DCHECK(initialized_.is_valid());
+
+ Data settings;
+ if (!OpenAndReadSettings(&settings))
+ return false;
+
+ *step = InRangeCast<int>(settings.backoff_step,
+ std::numeric_limits<int>::max());
+ return true;
+}
+
+bool Settings::SetBackoffStep(int step) {
+ DCHECK(initialized_.is_valid());
+
+ Data settings;
+ ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
+ if (!handle.is_valid())
+ return false;
+
+ settings.backoff_step = InRangeCast<uint32_t>(step, 0);
+
+ return WriteSettings(handle.get(), settings);
+}
+
// static
Settings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle(
FileHandle file,
diff --git crashpad/client/settings.h crashpad/client/settings.h
index b64f74f..0c3c22e 100644
--- crashpad/client/settings.h
+++ crashpad/client/settings.h
@@ -102,6 +102,11 @@ class Settings {
//! error logged.
bool SetLastUploadAttemptTime(time_t time);
+ bool GetNextUploadAttemptTime(time_t* time);
+ bool SetNextUploadAttemptTime(time_t time);
+ bool GetBackoffStep(int* step);
+ bool SetBackoffStep(int step);
+
private:
struct Data;
diff --git crashpad/handler/crash_report_upload_thread.h crashpad/handler/crash_report_upload_thread.h
index a9601d1..9517730 100644
--- crashpad/handler/crash_report_upload_thread.h
+++ crashpad/handler/crash_report_upload_thread.h
@@ -76,7 +76,7 @@ class CrashReportUploadThread : public WorkerThread::Delegate {
//! This method may be called from any thread.
void ReportPending();
- private:
+ protected:
//! \brief The result code from UploadReport().
enum class UploadResult {
//! \brief The crash report was uploaded successfully.
@@ -99,7 +99,7 @@ class CrashReportUploadThread : public WorkerThread::Delegate {
//! \brief Obtains all pending reports from the database, and calls
//! ProcessPendingReport() to process each one.
- void ProcessPendingReports();
+ virtual void ProcessPendingReports();
//! \brief Processes a single pending report from the database.
//!
@@ -113,7 +113,7 @@ class CrashReportUploadThread : public WorkerThread::Delegate {
//! remain in the “pending” state. If the upload fails and no more retries are
//! desired, or report upload is disabled, it will be marked as “completed” in
//! the database without ever having been uploaded.
- void ProcessPendingReport(const CrashReportDatabase::Report& report);
+ virtual void ProcessPendingReport(const CrashReportDatabase::Report& report);
//! \brief Attempts to upload a crash report.
//!
diff --git crashpad/handler/handler_main.cc crashpad/handler/handler_main.cc
index 29c5ddc..7a6bad7 100644
--- crashpad/handler/handler_main.cc
+++ crashpad/handler/handler_main.cc
@@ -29,8 +29,10 @@
#include "base/logging.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/scoped_generic.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
+#include "cef/libcef/features/features.h"
#include "client/crash_report_database.h"
#include "client/crashpad_client.h"
#include "client/prune_crash_reports.h"
@@ -62,6 +64,10 @@
#include "util/win/initial_client_data.h"
#endif // OS_MACOSX
+#if BUILDFLAG(ENABLE_CEF)
+#include "cef/libcef/common/cef_crash_report_upload_thread.h"
+#endif
+
namespace crashpad {
namespace {
@@ -168,6 +174,9 @@ int HandlerMain(int argc, char* argv[]) {
kOptionPipeName,
#endif // OS_MACOSX
kOptionURL,
+ kOptionMaxUploads,
+ kOptionMaxDatabaseSize,
+ kOptionMaxDatabaseAge,
// Standard options.
kOptionHelp = -2,
@@ -188,11 +197,17 @@ int HandlerMain(int argc, char* argv[]) {
InitialClientData initial_client_data;
#endif // OS_MACOSX
bool rate_limit;
+ int max_uploads;
+ int max_database_size;
+ int max_database_age;
} options = {};
#if defined(OS_MACOSX)
options.handshake_fd = -1;
#endif
options.rate_limit = true;
+ options.max_uploads = 0;
+ options.max_database_size = 0;
+ options.max_database_age = 0;
const option long_options[] = {
{"annotation", required_argument, nullptr, kOptionAnnotation},
@@ -222,6 +237,9 @@ int HandlerMain(int argc, char* argv[]) {
{"url", required_argument, nullptr, kOptionURL},
{"help", no_argument, nullptr, kOptionHelp},
{"version", no_argument, nullptr, kOptionVersion},
+ {"max-uploads", required_argument, nullptr, kOptionMaxUploads},
+ {"max-db-size", required_argument, nullptr, kOptionMaxDatabaseSize},
+ {"max-db-age", required_argument, nullptr, kOptionMaxDatabaseAge},
{nullptr, 0, nullptr, 0},
};
@@ -293,6 +311,27 @@ int HandlerMain(int argc, char* argv[]) {
options.url = optarg;
break;
}
+ case kOptionMaxUploads: {
+ if (base::StringToInt(optarg, &options.max_uploads)) {
+ if (options.max_uploads < 0)
+ options.max_uploads = 0;
+ }
+ break;
+ }
+ case kOptionMaxDatabaseSize: {
+ if (base::StringToInt(optarg, &options.max_database_size)) {
+ if (options.max_database_size < 0)
+ options.max_database_size = 0;
+ }
+ break;
+ }
+ case kOptionMaxDatabaseAge: {
+ if (base::StringToInt(optarg, &options.max_database_age)) {
+ if (options.max_database_age < 0)
+ options.max_database_age = 0;
+ }
+ break;
+ }
case kOptionHelp: {
Usage(me);
return EXIT_SUCCESS;
@@ -425,12 +464,18 @@ int HandlerMain(int argc, char* argv[]) {
// TODO(scottmg): options.rate_limit should be removed when we have a
// configurable database setting to control upload limiting.
// See https://crashpad.chromium.org/bug/23.
+#if BUILDFLAG(ENABLE_CEF)
+ CefCrashReportUploadThread upload_thread(
+ database.get(), options.url, options.rate_limit, options.max_uploads);
+#else
CrashReportUploadThread upload_thread(
database.get(), options.url, options.rate_limit);
+#endif
upload_thread.Start();
PruneCrashReportThread prune_thread(database.get(),
- PruneCondition::GetDefault());
+ PruneCondition::GetDefault(options.max_database_size,
+ options.max_database_age));
prune_thread.Start();
CrashReportExceptionHandler exception_handler(

View File

@ -4,6 +4,8 @@
#include "tests/shared/browser/client_app_browser.h"
#include "tests/cefclient/browser/client_browser.h"
#if defined(OS_LINUX)
#include "tests/cefclient/browser/print_handler_gtk.h"
#endif
@ -12,6 +14,7 @@ namespace client {
// static
void ClientAppBrowser::CreateDelegates(DelegateSet& delegates) {
browser::CreateDelegates(delegates);
}
// static

View File

@ -0,0 +1,40 @@
// Copyright (c) 2016 The Chromium Embedded Framework 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 "tests/cefclient/browser/client_browser.h"
#include "include/cef_crash_util.h"
namespace client {
namespace browser {
namespace {
class ClientBrowserDelegate : public ClientAppBrowser::Delegate {
public:
ClientBrowserDelegate() {}
void OnContextInitialized(CefRefPtr<ClientAppBrowser> app) OVERRIDE {
if (CefCrashReportingEnabled()) {
// Set some crash keys for testing purposes. Keys must be defined in the
// "crash_reporter.cfg" file. See cef_crash_util.h for details.
CefSetCrashKeyValue("testkey1", "value1_browser");
CefSetCrashKeyValue("testkey2", "value2_browser");
CefSetCrashKeyValue("testkey3", "value3_browser");
}
}
private:
DISALLOW_COPY_AND_ASSIGN(ClientBrowserDelegate);
IMPLEMENT_REFCOUNTING(ClientBrowserDelegate);
};
} // namespace
void CreateDelegates(ClientAppBrowser::DelegateSet& delegates) {
delegates.insert(new ClientBrowserDelegate);
}
} // namespace browser
} // namespace client

View File

@ -0,0 +1,21 @@
// Copyright (c) 2016 The Chromium Embedded Framework 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 CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_BROWSER_H_
#define CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_BROWSER_H_
#pragma once
#include "include/cef_base.h"
#include "tests/shared/browser/client_app_browser.h"
namespace client {
namespace browser {
// Create the browser delegate. Called from client_app_delegates_browser.cc.
void CreateDelegates(ClientAppBrowser::DelegateSet& delegates);
} // namespace browser
} // namespace client
#endif // CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_BROWSER_H_

View File

@ -7,6 +7,7 @@
#include <sstream>
#include <string>
#include "include/cef_crash_util.h"
#include "include/cef_dom.h"
#include "include/wrapper/cef_helpers.h"
#include "include/wrapper/cef_message_router.h"
@ -25,30 +26,42 @@ class ClientRenderDelegate : public ClientAppRenderer::Delegate {
: last_node_is_editable_(false) {
}
virtual void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) OVERRIDE {
void OnRenderThreadCreated(
CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefListValue> extra_info) OVERRIDE {
if (CefCrashReportingEnabled()) {
// Set some crash keys for testing purposes. Keys must be defined in the
// "crash_reporter.cfg" file. See cef_crash_util.h for details.
CefSetCrashKeyValue("testkey1", "value1_renderer");
CefSetCrashKeyValue("testkey2", "value2_renderer");
CefSetCrashKeyValue("testkey3", "value3_renderer");
}
}
void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) OVERRIDE {
// Create the renderer-side router for query handling.
CefMessageRouterConfig config;
message_router_ = CefMessageRouterRendererSide::Create(config);
}
virtual void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
message_router_->OnContextCreated(browser, frame, context);
}
virtual void OnContextReleased(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
void OnContextReleased(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
message_router_->OnContextReleased(browser, frame, context);
}
virtual void OnFocusedNodeChanged(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefDOMNode> node) OVERRIDE {
void OnFocusedNodeChanged(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefDOMNode> node) OVERRIDE {
bool is_editable = (node.get() && node->IsEditable());
if (is_editable != last_node_is_editable_) {
// Notify the browser of the change in focused element type.
@ -60,7 +73,7 @@ class ClientRenderDelegate : public ClientAppRenderer::Delegate {
}
}
virtual bool OnProcessMessageReceived(
bool OnProcessMessageReceived(
CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
@ -75,6 +88,7 @@ class ClientRenderDelegate : public ClientAppRenderer::Delegate {
// Handles the renderer side of query routing.
CefRefPtr<CefMessageRouterRendererSide> message_router_;
DISALLOW_COPY_AND_ASSIGN(ClientRenderDelegate);
IMPLEMENT_REFCOUNTING(ClientRenderDelegate);
};

235
tools/crash_server.py Normal file
View File

@ -0,0 +1,235 @@
#!/usr/bin/env python
# Copyright 2017 The Chromium Embedded Framework Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be found
# in the LICENSE file.
"""
This script implements a simple HTTP server for receiving crash report uploads
from a Breakpad/Crashpad client (any CEF-based application). This script is
intended for testing purposes only. An HTTPS server and a system such as Socorro
(https://wiki.mozilla.org/Socorro) should be used when uploading crash reports
from production applications.
Usage of this script is as follows:
1. Run this script from the command-line. The first argument is the server port
number and the second argument is the directory where uploaded report
information will be saved:
> python crash_server.py 8080 /path/to/dumps
2. Create a "crash_reporter.cfg" file at the required platform-specific
location. On Windows and Linux this file must be placed next to the main
application executable. On macOS this file must be placed in the top-level
app bundle Resources directory (e.g. "<appname>.app/Contents/Resources"). At
a minimum it must contain a "ServerURL=http://localhost:8080" line under the
"[Config]" section (make sure the port number matches the value specified in
step 1). See comments in include/cef_crash_util.h for a complete
specification of this file.
Example file contents:
[Config]
ServerURL=http://localhost:8080
# Disable rate limiting so that all crashes are uploaded.
RateLimitEnabled=false
MaxUploadsPerDay=0
[CrashKeys]
# The cefclient sample application sets these values (see step 5 below).
testkey1=small
testkey2=medium
testkey3=large
3. Load one of the following URLs in the CEF-based application to cause a crash:
Main (browser) process crash: chrome://inducebrowsercrashforrealz
Renderer process crash: chrome://crash
GPU process crash: chrome://gpucrash
4. When this script successfully receives a crash report upload you will see
console output like the following:
01/10/2017 12:31:23: Dump <id>
The "<id>" value is a 16 digit hexadecimal string that uniquely identifies
the dump. Crash dumps and metadata (product state, command-line flags, crash
keys, etc.) will be written to the "<id>.dmp" and "<id>.json" files
underneath the directory specified in step 1.
On Linux Breakpad uses the wget utility to upload crash dumps, so make sure
that utility is installed. If the crash is handled correctly then you should
see console output like the following when the client uploads a crash dump:
--2017-01-10 12:31:22-- http://localhost:8080/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:8080... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: '/dev/fd/3'
Crash dump id: <id>
On macOS when uploading a crash report to this script over HTTP you may
receive an error like the following:
"Transport security has blocked a cleartext HTTP (http://) resource load
since it is insecure. Temporary exceptions can be configured via your app's
Info.plist file."
You can work around this error by adding the following key to the Helper app
Info.plist file (e.g. "<appname>.app/Contents/Frameworks/
<appname> Helper.app/Contents/Info.plist"):
<key>NSAppTransportSecurity</key>
<dict>
<!--Allow all connections (for testing only!)-->
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
5. The cefclient sample application sets test crash key values in the browser
and renderer processes. To work properly these values must also be defined
in the "[CrashKeys]" section of "crash_reporter.cfg" as shown above.
In tests/cefclient/browser/client_browser.cc (browser process):
CefSetCrashKeyValue("testkey1", "value1_browser");
CefSetCrashKeyValue("testkey2", "value2_browser");
CefSetCrashKeyValue("testkey3", "value3_browser");
In tests/cefclient/renderer/client_renderer.cc (renderer process):
CefSetCrashKeyValue("testkey1", "value1_renderer");
CefSetCrashKeyValue("testkey2", "value2_renderer");
CefSetCrashKeyValue("testkey3", "value3_renderer");
When crashing the browser or renderer processes with cefclient you should
verify that the test crash key values are included in the metadata
("<id>.json") file. Some values may be chunked as described in
include/cef_crash_util.h.
"""
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import cgi
import cStringIO
import datetime
import json
import os
import shutil
import sys
import uuid
import zlib
def print_msg(msg):
""" Write |msg| to stdout and flush. """
timestr = datetime.datetime.now().strftime("%m/%d/%Y %H:%M:%S")
sys.stdout.write("%s: %s\n" % (timestr, msg))
sys.stdout.flush()
# Key identifying the minidump file.
minidump_key = 'upload_file_minidump'
class CrashHTTPRequestHandler(BaseHTTPRequestHandler):
def __init__(self, dump_directory, *args):
self._dump_directory = dump_directory
BaseHTTPRequestHandler.__init__(self, *args)
def _send_default_response_headers(self):
""" Send default response headers. """
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def _parse_post_data(self):
""" Returns a cgi.FieldStorage object for this request or None if this is
not a POST request. """
if self.command != 'POST':
return None
return cgi.FieldStorage(
fp = self.rfile,
headers = self.headers,
environ = {
'REQUEST_METHOD': 'POST',
'CONTENT_TYPE': self.headers['Content-Type'],
})
def _create_new_dump_id(self):
""" Breakpad requires a 16 digit hexadecimal dump ID. """
return str(uuid.uuid4().get_hex().upper()[0:16])
def do_GET(self):
""" Default empty implementation for handling GET requests. """
self._send_default_response_headers()
self.wfile.write("<html><body><h1>GET!</h1></body></html>")
def do_HEAD(self):
""" Default empty implementation for handling HEAD requests. """
self._send_default_response_headers()
def do_POST(self):
""" Handle a multi-part POST request submitted by Breakpad/Crashpad. """
self._send_default_response_headers()
# Create a unique ID for the dump.
dump_id = self._create_new_dump_id()
# Return the unique ID to the caller.
self.wfile.write(dump_id)
dmp_stream = None
metadata = {}
# Breakpad on Linux sends gzipped request contents.
if 'Content-Encoding' in self.headers and self.headers['Content-Encoding'] == 'gzip':
print_msg('Decompressing gzipped request')
self.rfile = cStringIO.StringIO(zlib.decompress(self.rfile.read(), 16+zlib.MAX_WBITS))
# Parse the multi-part request.
form_data = self._parse_post_data()
for key in form_data.keys():
if key == minidump_key and form_data[minidump_key].file:
dmp_stream = form_data[minidump_key].file
else:
metadata[key] = form_data[key].value
if dmp_stream is None:
# Exit early if the request is invalid.
print_msg('Invalid dump %s' % dump_id)
return
print_msg('Dump %s' % dump_id)
# Write the minidump to file.
dump_file = os.path.join(self._dump_directory, dump_id + '.dmp')
with open(dump_file, 'wb') as fp:
shutil.copyfileobj(dmp_stream, fp)
# Write the metadata to file.
meta_file = os.path.join(self._dump_directory, dump_id + '.json')
with open(meta_file, 'w') as fp:
json.dump(metadata, fp)
def HandleRequestsUsing(dump_store):
return lambda *args: CrashHTTPRequestHandler(dump_directory, *args)
def RunCrashServer(port, dump_directory):
""" Run the crash handler HTTP server. """
httpd = HTTPServer(('', port), HandleRequestsUsing(dump_directory))
print_msg('Starting httpd on port %d' % port)
httpd.serve_forever()
# Program entry point.
if __name__ == "__main__":
if len(sys.argv) != 3:
print 'Usage: %s <port> <dump_directory>' % os.path.basename(sys.argv[0])
sys.exit(1)
# Create the dump directory if necessary.
dump_directory = sys.argv[2]
if not os.path.exists(dump_directory):
os.makedirs(dump_directory)
if not os.path.isdir(dump_directory):
raise Exception('Directory does not exist: %s' % dump_directory)
RunCrashServer(int(sys.argv[1]), dump_directory)