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:
parent
c990b5d877
commit
a834487177
14
BUILD.gn
14
BUILD.gn
|
@ -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" ]
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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_
|
|
@ -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_
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()) {
|
||||
// Don’t attempt an upload if there’s no URL or if uploads have been
|
||||
// disabled in the database’s 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 it’s 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);
|
||||
}
|
|
@ -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_
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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",
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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/',
|
||||
},
|
||||
]
|
||||
|
|
|
@ -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;
|
||||
|
|
@ -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(
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
Loading…
Reference in New Issue