262 lines
8.6 KiB
C++
262 lines
8.6 KiB
C++
// 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/stl_util.h"
|
|
#include "base/strings/string_piece.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "chrome/common/crash_keys.h"
|
|
#include "components/crash/core/common/crash_key.h"
|
|
#include "components/crash/core/common/crash_keys.h"
|
|
#include "content/public/common/content_switches.h"
|
|
#include "services/service_manager/embedder/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"
|
|
#include "v8/include/v8-wasm-trap-handler-posix.h"
|
|
#endif
|
|
|
|
namespace crash_reporting {
|
|
|
|
namespace {
|
|
|
|
#if defined(OS_WIN)
|
|
|
|
const base::FilePath::CharType kChromeElfDllName[] =
|
|
FILE_PATH_LITERAL("chrome_elf.dll");
|
|
|
|
// exported in crash_reporter_client.cc:
|
|
// int __declspec(dllexport) __cdecl SetCrashKeyValueImpl.
|
|
typedef int(__cdecl* SetCrashKeyValue)(const char*,
|
|
size_t,
|
|
const char*,
|
|
size_t);
|
|
|
|
// int __declspec(dllexport) __cdecl IsCrashReportingEnabledImpl.
|
|
typedef int(__cdecl* IsCrashReportingEnabled)();
|
|
|
|
bool SetCrashKeyValueTrampoline(const base::StringPiece& key,
|
|
const base::StringPiece& value) {
|
|
static SetCrashKeyValue set_crash_key = []() {
|
|
HMODULE elf_module = GetModuleHandle(kChromeElfDllName);
|
|
return reinterpret_cast<SetCrashKeyValue>(
|
|
elf_module ? GetProcAddress(elf_module, "SetCrashKeyValueImpl")
|
|
: nullptr);
|
|
}();
|
|
if (set_crash_key) {
|
|
return !!(set_crash_key)(key.data(), key.size(), value.data(),
|
|
value.size());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsCrashReportingEnabledTrampoline() {
|
|
static IsCrashReportingEnabled is_crash_reporting_enabled = []() {
|
|
HMODULE elf_module = GetModuleHandle(kChromeElfDllName);
|
|
return reinterpret_cast<IsCrashReportingEnabled>(
|
|
elf_module ? GetProcAddress(elf_module, "IsCrashReportingEnabledImpl")
|
|
: nullptr);
|
|
}();
|
|
if (is_crash_reporting_enabled) {
|
|
return !!(is_crash_reporting_enabled)();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endif // defined(OS_WIN)
|
|
|
|
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);
|
|
|
|
if (base::mac::AmIBundled()) {
|
|
// 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 != service_manager::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-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 < base::size(kIgnoreSwitches); ++i) {
|
|
if (flag.compare(2, len, kIgnoreSwitches[i]) == 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool Enabled() {
|
|
return g_crash_reporting_enabled;
|
|
}
|
|
|
|
bool SetCrashKeyValue(const base::StringPiece& key,
|
|
const base::StringPiece& value) {
|
|
if (!g_crash_reporting_enabled)
|
|
return false;
|
|
|
|
#if defined(OS_WIN)
|
|
return SetCrashKeyValueTrampoline(key, value);
|
|
#else
|
|
return g_crash_reporter_client.Pointer()->SetCrashKeyValue(key, value);
|
|
#endif
|
|
}
|
|
|
|
#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);
|
|
|
|
breakpad::SetFirstChanceExceptionHandler(v8::TryHandleWebAssemblyTrapPosix);
|
|
#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)
|
|
g_crash_reporting_enabled = IsCrashReportingEnabledTrampoline();
|
|
#endif
|
|
|
|
if (g_crash_reporting_enabled) {
|
|
LOG(INFO) << "Crash reporting enabled for process: "
|
|
<< (process_type.empty() ? "browser" : process_type.c_str());
|
|
}
|
|
|
|
crash_reporter::InitializeCrashKeys();
|
|
|
|
// 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::Enabled();
|
|
}
|
|
|
|
void CefSetCrashKeyValue(const CefString& key, const CefString& value) {
|
|
if (!crash_reporting::SetCrashKeyValue(key.ToString(), value.ToString())) {
|
|
LOG(WARNING) << "Failed to set crash key: " << key.ToString()
|
|
<< " with value: " << value.ToString();
|
|
}
|
|
}
|