Improve crashpad integration (issue #1995)

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,42 @@
// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
#define CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_
#include "third_party/crashpad/crashpad/handler/crash_report_upload_thread.h"
class CefCrashReportUploadThread : public crashpad::CrashReportUploadThread {
public:
CefCrashReportUploadThread(crashpad::CrashReportDatabase* database,
const std::string& url,
bool rate_limit,
int max_uploads);
~CefCrashReportUploadThread();
private:
void ProcessPendingReports() override;
void ProcessPendingReport(
const crashpad::CrashReportDatabase::Report& report) override;
bool UploadsEnabled() const;
bool MaxUploadsEnabled() const;
bool MaxUploadsExceeded() const;
bool BackoffPending() const;
void IncreaseBackoff();
void ResetBackoff();
int max_uploads_;
// Track the number of uploads that have completed within the last 24 hours.
// Only used when RateLimitEnabled() is true. Value is reset each time
// ProcessPendingReports() is called.
int recent_upload_ct_ = 0;
DISALLOW_COPY_AND_ASSIGN(CefCrashReportUploadThread);
};
#endif // CEF_LIBCEF_COMMON_CEF_CRASH_REPORT_UPLOAD_THREAD_H_

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,29 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2016 The Chromium Authors. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.
#include <string>
#include "build/build_config.h"
namespace base {
class CommandLine;
}
namespace crash_reporting {
// Functions are called from similarly named methods in CefMainDelegate.
#if defined(OS_POSIX)
void BasicStartupComplete(base::CommandLine* command_line);
#endif
void PreSandboxStartup(const base::CommandLine& command_line,
const std::string& process_type);
#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
void ZygoteForked(base::CommandLine* command_line,
const std::string& process_type);
#endif
} // namespace crash_reporting

View File

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

View File

@@ -0,0 +1,11 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2016 The Chromium Authors. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.
namespace crash_reporting_win {
// Called from libcef to initialize crash key globals. Retrieves the necessary
// state from chrome_elf via exported functions.
bool InitializeCrashReportingForModule();
} // namespace crash_reporting_win

View File

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

View File

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

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

@@ -0,0 +1,19 @@
# Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/buildflag_header.gni")
import("//cef/libcef/features/features.gni")
# This file is in a separate directory so all targets in the build can refer to
# the buildflag header to get the necessary preprocessor defines without
# bringing in any CEF targets. Other targets can depend on this target
# regardless of whether CEF is being built.
buildflag_header("features") {
header = "features.h"
flags = [
"ENABLE_CEF=$enable_cef",
]
}

View File

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