mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
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:
@@ -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,
|
||||
|
247
libcef/common/cef_crash_report_upload_thread.cc
Normal file
247
libcef/common/cef_crash_report_upload_thread.cc
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "libcef/common/cef_crash_report_upload_thread.h"
|
||||
|
||||
#include "third_party/crashpad/crashpad/client/settings.h"
|
||||
|
||||
using namespace crashpad;
|
||||
|
||||
namespace {
|
||||
|
||||
// Calls CrashReportDatabase::RecordUploadAttempt() with |successful| set to
|
||||
// false upon destruction unless disarmed by calling Fire() or Disarm(). Fire()
|
||||
// triggers an immediate call. Armed upon construction.
|
||||
class CallRecordUploadAttempt {
|
||||
public:
|
||||
CallRecordUploadAttempt(CrashReportDatabase* database,
|
||||
const CrashReportDatabase::Report* report)
|
||||
: database_(database),
|
||||
report_(report) {
|
||||
}
|
||||
|
||||
~CallRecordUploadAttempt() {
|
||||
Fire();
|
||||
}
|
||||
|
||||
void Fire() {
|
||||
if (report_) {
|
||||
database_->RecordUploadAttempt(report_, false, std::string());
|
||||
}
|
||||
|
||||
Disarm();
|
||||
}
|
||||
|
||||
void Disarm() {
|
||||
report_ = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
CrashReportDatabase* database_; // weak
|
||||
const CrashReportDatabase::Report* report_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CallRecordUploadAttempt);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
CefCrashReportUploadThread::CefCrashReportUploadThread(
|
||||
CrashReportDatabase* database,
|
||||
const std::string& url,
|
||||
bool rate_limit,
|
||||
int max_uploads)
|
||||
: CrashReportUploadThread(database, url, rate_limit),
|
||||
max_uploads_(max_uploads) {
|
||||
}
|
||||
|
||||
CefCrashReportUploadThread::~CefCrashReportUploadThread() {
|
||||
}
|
||||
|
||||
void CefCrashReportUploadThread::ProcessPendingReports() {
|
||||
if (BackoffPending()) {
|
||||
// Try again later.
|
||||
return;
|
||||
}
|
||||
|
||||
if (MaxUploadsEnabled()) {
|
||||
// Retrieve all completed reports.
|
||||
std::vector<CrashReportDatabase::Report> reports;
|
||||
if (database_->GetCompletedReports(&reports) !=
|
||||
CrashReportDatabase::kNoError) {
|
||||
// The database is sick. It might be prudent to stop trying to poke it
|
||||
// from this thread by abandoning the thread altogether. On the other
|
||||
// hand, if the problem is transient, it might be possible to talk to it
|
||||
// again on the next pass. For now, take the latter approach.
|
||||
return;
|
||||
}
|
||||
|
||||
const time_t now = time(nullptr);
|
||||
const int kSeconds = 60 * 60 * 24; // 24 hours
|
||||
|
||||
// Count how many reports have completed in the last 24 hours.
|
||||
recent_upload_ct_ = 0;
|
||||
for (const CrashReportDatabase::Report& report : reports) {
|
||||
if (report.last_upload_attempt_time > now - kSeconds)
|
||||
recent_upload_ct_++;
|
||||
}
|
||||
}
|
||||
|
||||
// Continue with processing pending reports.
|
||||
CrashReportUploadThread::ProcessPendingReports();
|
||||
}
|
||||
|
||||
void CefCrashReportUploadThread::ProcessPendingReport(
|
||||
const CrashReportDatabase::Report& report) {
|
||||
// Always allow upload if it's been explicitly requested by the user.
|
||||
if (!report.upload_explicitly_requested) {
|
||||
if (!UploadsEnabled()) {
|
||||
// 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);
|
||||
}
|
42
libcef/common/cef_crash_report_upload_thread.h
Normal file
42
libcef/common/cef_crash_report_upload_thread.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
|
||||
#define CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
|
||||
|
||||
#include "third_party/crashpad/crashpad/handler/crash_report_upload_thread.h"
|
||||
|
||||
class CefCrashReportUploadThread : public crashpad::CrashReportUploadThread {
|
||||
public:
|
||||
CefCrashReportUploadThread(crashpad::CrashReportDatabase* database,
|
||||
const std::string& url,
|
||||
bool rate_limit,
|
||||
int max_uploads);
|
||||
~CefCrashReportUploadThread();
|
||||
|
||||
private:
|
||||
void ProcessPendingReports() override;
|
||||
void ProcessPendingReport(
|
||||
const crashpad::CrashReportDatabase::Report& report) override;
|
||||
|
||||
bool UploadsEnabled() const;
|
||||
|
||||
bool MaxUploadsEnabled() const;
|
||||
bool MaxUploadsExceeded() const;
|
||||
|
||||
bool BackoffPending() const;
|
||||
void IncreaseBackoff();
|
||||
void ResetBackoff();
|
||||
|
||||
int max_uploads_;
|
||||
|
||||
// Track the number of uploads that have completed within the last 24 hours.
|
||||
// Only used when RateLimitEnabled() is true. Value is reset each time
|
||||
// ProcessPendingReports() is called.
|
||||
int recent_upload_ct_ = 0;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefCrashReportUploadThread);
|
||||
};
|
||||
|
||||
#endif // CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
|
@@ -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);
|
||||
};
|
||||
|
||||
|
190
libcef/common/crash_reporting.cc
Normal file
190
libcef/common/crash_reporting.cc
Normal file
@@ -0,0 +1,190 @@
|
||||
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 2016 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "libcef/common/crash_reporting.h"
|
||||
|
||||
#include "include/cef_crash_util.h"
|
||||
#include "libcef/common/cef_switches.h"
|
||||
|
||||
#include "base/base_switches.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/debug/crash_logging.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "chrome/common/crash_keys.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include "base/mac/foundation_util.h"
|
||||
#include "components/crash/content/app/crashpad.h"
|
||||
#include "components/crash/core/common/crash_keys.h"
|
||||
#include "content/public/common/content_paths.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
#include "base/lazy_instance.h"
|
||||
#include "libcef/common/crash_reporter_client.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_MACOSX)
|
||||
#include "components/crash/content/app/breakpad_linux.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "libcef/common/crash_reporting_win.h"
|
||||
#endif
|
||||
|
||||
namespace crash_reporting {
|
||||
|
||||
namespace {
|
||||
|
||||
bool g_crash_reporting_enabled = false;
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
base::LazyInstance<CefCrashReporterClient>::Leaky g_crash_reporter_client =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
void InitCrashReporter(const base::CommandLine& command_line,
|
||||
const std::string& process_type) {
|
||||
CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
|
||||
if (!crash_client->HasCrashConfigFile())
|
||||
return;
|
||||
|
||||
crash_reporter::SetCrashReporterClient(crash_client);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// TODO(mark): Right now, InitializeCrashpad() needs to be called after
|
||||
// CommandLine::Init() and configuration of chrome::DIR_CRASH_DUMPS. Ideally,
|
||||
// Crashpad initialization could occur sooner, preferably even before the
|
||||
// framework dylib is even loaded, to catch potential early crashes.
|
||||
crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
|
||||
|
||||
// Mac Chrome is packaged with a main app bundle and a helper app bundle.
|
||||
// The main app bundle should only be used for the browser process, so it
|
||||
// should never see a --type switch (switches::kProcessType). Likewise,
|
||||
// the helper should always have a --type switch.
|
||||
//
|
||||
// This check is done this late so there is already a call to
|
||||
// base::mac::IsBackgroundOnlyProcess(), so there is no change in
|
||||
// startup/initialization order.
|
||||
|
||||
// The helper's Info.plist marks it as a background only app.
|
||||
if (base::mac::IsBackgroundOnlyProcess()) {
|
||||
CHECK(command_line.HasSwitch(switches::kProcessType) &&
|
||||
!process_type.empty())
|
||||
<< "Helper application requires --type.";
|
||||
} else {
|
||||
CHECK(!command_line.HasSwitch(switches::kProcessType) &&
|
||||
process_type.empty())
|
||||
<< "Main application forbids --type, saw " << process_type;
|
||||
}
|
||||
|
||||
g_crash_reporting_enabled = true;
|
||||
#else // !defined(OS_MACOSX)
|
||||
breakpad::SetCrashServerURL(crash_client->GetCrashServerURL());
|
||||
|
||||
if (process_type != switches::kZygoteProcess) {
|
||||
// Crash reporting for subprocesses created using the zygote will be
|
||||
// initialized in ZygoteForked.
|
||||
breakpad::InitCrashReporter(process_type);
|
||||
|
||||
g_crash_reporting_enabled = true;
|
||||
}
|
||||
#endif // !defined(OS_MACOSX)
|
||||
}
|
||||
#endif // defined(OS_POSIX)
|
||||
|
||||
// Used to exclude command-line flags from crash reporting.
|
||||
bool IsBoringCEFSwitch(const std::string& flag) {
|
||||
if (crash_keys::IsBoringChromeSwitch(flag))
|
||||
return true;
|
||||
|
||||
static const char* const kIgnoreSwitches[] = {
|
||||
// CEF internals.
|
||||
switches::kLogFile,
|
||||
|
||||
// Chromium internals.
|
||||
"content-image-texture-target",
|
||||
"mojo-platform-channel-handle",
|
||||
"primordial-pipe-token",
|
||||
"service-request-channel-token",
|
||||
};
|
||||
|
||||
if (!base::StartsWith(flag, "--", base::CompareCase::SENSITIVE))
|
||||
return false;
|
||||
|
||||
size_t end = flag.find("=");
|
||||
size_t len = (end == std::string::npos) ? flag.length() - 2 : end - 2;
|
||||
for (size_t i = 0; i < arraysize(kIgnoreSwitches); ++i) {
|
||||
if (flag.compare(2, len, kIgnoreSwitches[i]) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
// Be aware that logging is not initialized at the time this method is called.
|
||||
void BasicStartupComplete(base::CommandLine* command_line) {
|
||||
CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
|
||||
if (crash_client->ReadCrashConfigFile()) {
|
||||
#if !defined(OS_MACOSX)
|
||||
// Breakpad requires this switch.
|
||||
command_line->AppendSwitch(switches::kEnableCrashReporter);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void PreSandboxStartup(const base::CommandLine& command_line,
|
||||
const std::string& process_type) {
|
||||
#if defined(OS_POSIX)
|
||||
// Initialize crash reporting here on macOS and Linux. Crash reporting on
|
||||
// Windows is initialized from context.cc.
|
||||
InitCrashReporter(command_line, process_type);
|
||||
#elif defined(OS_WIN)
|
||||
// Initialize crash key globals in the module (libcef) address space.
|
||||
g_crash_reporting_enabled =
|
||||
crash_reporting_win::InitializeCrashReportingForModule();
|
||||
#endif
|
||||
|
||||
if (g_crash_reporting_enabled) {
|
||||
LOG(INFO) << "Crash reporting enabled for process: " <<
|
||||
(process_type.empty() ? "browser" : process_type.c_str());
|
||||
}
|
||||
|
||||
// After platform crash reporting have been initialized, store the command
|
||||
// line for crash reporting.
|
||||
crash_keys::SetSwitchesFromCommandLine(command_line, &IsBoringCEFSwitch);
|
||||
}
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
|
||||
void ZygoteForked(base::CommandLine* command_line,
|
||||
const std::string& process_type) {
|
||||
CefCrashReporterClient* crash_client = g_crash_reporter_client.Pointer();
|
||||
if (crash_client->HasCrashConfigFile()) {
|
||||
// Breakpad requires this switch.
|
||||
command_line->AppendSwitch(switches::kEnableCrashReporter);
|
||||
}
|
||||
|
||||
InitCrashReporter(*command_line, process_type);
|
||||
|
||||
if (g_crash_reporting_enabled) {
|
||||
LOG(INFO) << "Crash reporting enabled for process: " << process_type;
|
||||
}
|
||||
|
||||
// Reset the command line for the newly spawned process.
|
||||
crash_keys::SetSwitchesFromCommandLine(*command_line, &IsBoringCEFSwitch);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace crash_reporting
|
||||
|
||||
bool CefCrashReportingEnabled() {
|
||||
return crash_reporting::g_crash_reporting_enabled;
|
||||
}
|
||||
|
||||
void CefSetCrashKeyValue(const CefString& key, const CefString& value) {
|
||||
base::debug::SetCrashKeyValue(key.ToString(), value.ToString());
|
||||
}
|
29
libcef/common/crash_reporting.h
Normal file
29
libcef/common/crash_reporting.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 2016 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
class CommandLine;
|
||||
}
|
||||
|
||||
namespace crash_reporting {
|
||||
|
||||
// Functions are called from similarly named methods in CefMainDelegate.
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
void BasicStartupComplete(base::CommandLine* command_line);
|
||||
#endif
|
||||
|
||||
void PreSandboxStartup(const base::CommandLine& command_line,
|
||||
const std::string& process_type);
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
|
||||
void ZygoteForked(base::CommandLine* command_line,
|
||||
const std::string& process_type);
|
||||
#endif
|
||||
|
||||
} // namespace crash_reporting
|
120
libcef/common/crash_reporting_win.cc
Normal file
120
libcef/common/crash_reporting_win.cc
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 2016 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "libcef/common/crash_reporting_win.h"
|
||||
|
||||
#include "base/debug/crash_logging.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/common/chrome_constants.h"
|
||||
#include "components/crash/core/common/crash_keys.h"
|
||||
|
||||
namespace crash_reporting_win {
|
||||
|
||||
namespace {
|
||||
|
||||
// exported in crash_reporter_client.cc:
|
||||
// size_t __declspec(dllexport) __cdecl GetCrashKeyCountImpl.
|
||||
typedef size_t(__cdecl* GetCrashKeyCount)();
|
||||
|
||||
// exported in crash_reporter_client.cc:
|
||||
// bool __declspec(dllexport) __cdecl GetCrashKeyImpl.
|
||||
typedef bool(__cdecl* GetCrashKey)(size_t, const char**, size_t*);
|
||||
|
||||
size_t GetCrashKeyCountTrampoline() {
|
||||
static GetCrashKeyCount get_crash_key_count = []() {
|
||||
HMODULE elf_module = GetModuleHandle(chrome::kChromeElfDllName);
|
||||
return reinterpret_cast<GetCrashKeyCount>(
|
||||
elf_module ? GetProcAddress(elf_module, "GetCrashKeyCountImpl")
|
||||
: nullptr);
|
||||
}();
|
||||
if (get_crash_key_count) {
|
||||
return (get_crash_key_count)();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GetCrashKeyTrampoline(size_t index,
|
||||
const char** key_name,
|
||||
size_t* max_length) {
|
||||
static GetCrashKey get_crash_key = []() {
|
||||
HMODULE elf_module = GetModuleHandle(chrome::kChromeElfDllName);
|
||||
return reinterpret_cast<GetCrashKey>(
|
||||
elf_module ? GetProcAddress(elf_module, "GetCrashKeyImpl")
|
||||
: nullptr);
|
||||
}();
|
||||
if (get_crash_key) {
|
||||
return (get_crash_key)(index, key_name, max_length);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// From chrome/common/child_process_logging_win.cc:
|
||||
|
||||
// exported in breakpad_win.cc/crashpad_win.cc:
|
||||
// void __declspec(dllexport) __cdecl SetCrashKeyValueImpl.
|
||||
typedef void(__cdecl* SetCrashKeyValue)(const wchar_t*, const wchar_t*);
|
||||
|
||||
// exported in breakpad_win.cc/crashpad_win.cc:
|
||||
// void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl.
|
||||
typedef void(__cdecl* ClearCrashKeyValue)(const wchar_t*);
|
||||
|
||||
void SetCrashKeyValueTrampoline(const base::StringPiece& key,
|
||||
const base::StringPiece& value) {
|
||||
static SetCrashKeyValue set_crash_key = []() {
|
||||
HMODULE elf_module = GetModuleHandle(chrome::kChromeElfDllName);
|
||||
return reinterpret_cast<SetCrashKeyValue>(
|
||||
elf_module ? GetProcAddress(elf_module, "SetCrashKeyValueImpl")
|
||||
: nullptr);
|
||||
}();
|
||||
if (set_crash_key) {
|
||||
(set_crash_key)(base::UTF8ToWide(key).data(),
|
||||
base::UTF8ToWide(value).data());
|
||||
}
|
||||
}
|
||||
|
||||
void ClearCrashKeyValueTrampoline(const base::StringPiece& key) {
|
||||
static ClearCrashKeyValue clear_crash_key = []() {
|
||||
HMODULE elf_module = GetModuleHandle(chrome::kChromeElfDllName);
|
||||
return reinterpret_cast<ClearCrashKeyValue>(
|
||||
elf_module ? GetProcAddress(elf_module, "ClearCrashKeyValueImpl")
|
||||
: nullptr);
|
||||
}();
|
||||
if (clear_crash_key)
|
||||
(clear_crash_key)(base::UTF8ToWide(key).data());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool InitializeCrashReportingForModule() {
|
||||
base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueTrampoline,
|
||||
&ClearCrashKeyValueTrampoline);
|
||||
|
||||
std::vector<base::debug::CrashKey> keys;
|
||||
|
||||
size_t key_ct = GetCrashKeyCountTrampoline();
|
||||
if (key_ct > 0U) {
|
||||
keys.reserve(key_ct);
|
||||
|
||||
const char* key_name;
|
||||
size_t max_length;
|
||||
|
||||
for (size_t i = 0; i < key_ct; ++i) {
|
||||
if (GetCrashKeyTrampoline(i, &key_name, &max_length))
|
||||
keys.push_back({key_name, max_length});
|
||||
}
|
||||
}
|
||||
|
||||
if (!keys.empty()) {
|
||||
base::debug::InitCrashKeys(&keys[0], keys.size(),
|
||||
crash_keys::kChunkMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace crash_reporting_win
|
11
libcef/common/crash_reporting_win.h
Normal file
11
libcef/common/crash_reporting_win.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 2016 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
namespace crash_reporting_win {
|
||||
|
||||
// Called from libcef to initialize crash key globals. Retrieves the necessary
|
||||
// state from chrome_elf via exported functions.
|
||||
bool InitializeCrashReportingForModule();
|
||||
|
||||
} // namespace crash_reporting_win
|
@@ -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_;
|
||||
|
||||
|
19
libcef/features/BUILD.gn
Normal file
19
libcef/features/BUILD.gn
Normal file
@@ -0,0 +1,19 @@
|
||||
# Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//build/buildflag_header.gni")
|
||||
import("//cef/libcef/features/features.gni")
|
||||
|
||||
# This file is in a separate directory so all targets in the build can refer to
|
||||
# the buildflag header to get the necessary preprocessor defines without
|
||||
# bringing in any CEF targets. Other targets can depend on this target
|
||||
# regardless of whether CEF is being built.
|
||||
|
||||
buildflag_header("features") {
|
||||
header = "features.h"
|
||||
|
||||
flags = [
|
||||
"ENABLE_CEF=$enable_cef",
|
||||
]
|
||||
}
|
7
libcef/features/features.gni
Normal file
7
libcef/features/features.gni
Normal file
@@ -0,0 +1,7 @@
|
||||
# Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
declare_args() {
|
||||
enable_cef = true
|
||||
}
|
Reference in New Issue
Block a user