blob: 3298bd1c8385902fdfef82f900c9e83f15634b5c [file] [log] [blame]
// Copyright 2015 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 "android_webview/common/crash_reporter/aw_crash_reporter_client.h"
#include <random>
#include "android_webview/common/aw_channel.h"
#include "android_webview/common/aw_descriptors.h"
#include "android_webview/common/aw_paths.h"
#include "android_webview/common/crash_reporter/crash_keys.h"
#include "base/android/build_info.h"
#include "base/base_paths_android.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/path_service.h"
#include "base/scoped_native_library.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/crash/content/app/breakpad_linux.h"
#include "components/crash/content/app/crash_reporter_client.h"
#include "components/version_info/version_info.h"
#include "components/version_info/version_info_values.h"
#include "content/public/common/content_switches.h"
namespace android_webview {
namespace crash_reporter {
namespace {
// TODO(gsennton) lower the following value before pushing to stable:
const double minidump_generation_user_fraction = 1.0;
// Returns whether the current process should be reporting crashes through
// minidumps. This function should only be called once per process - the return
// value might differ between different calls to this function.
bool is_process_in_crash_reporting_sample() {
// TODO(gsennton): update this method to depend on finch when that is
// implemented for WebView.
base::Time cur_time = base::Time::Now();
std::minstd_rand generator(cur_time.ToInternalValue() /* seed */);
std::uniform_real_distribution<double> distribution(0.0, 1.0);
return minidump_generation_user_fraction > distribution(generator);
}
class AwCrashReporterClient : public ::crash_reporter::CrashReporterClient {
public:
AwCrashReporterClient()
: dump_fd_(kAndroidMinidumpDescriptor),
crash_signal_fd_(-1),
in_crash_reporting_sample_(is_process_in_crash_reporting_sample()) {}
// Does not use lock, can only be called immediately after creation.
void set_crash_signal_fd(int fd) { crash_signal_fd_ = fd; }
// crash_reporter::CrashReporterClient implementation.
bool UseCrashKeysWhiteList() override { return true; }
const char* const* GetCrashKeyWhiteList() override;
bool IsRunningUnattended() override { return false; }
bool GetCollectStatsConsent() override;
void GetProductNameAndVersion(const char** product_name,
const char** version) override {
*product_name = "AndroidWebView";
*version = PRODUCT_VERSION;
}
void GetProductNameAndVersion(std::string* product_name,
std::string* version,
std::string* channel) override {
*product_name = "AndroidWebView";
*version = PRODUCT_VERSION;
*channel =
version_info::GetChannelString(android_webview::GetChannelOrStable());
}
// Microdumps are always enabled in WebView builds, conversely to what happens
// in the case of the other Chrome for Android builds (where they are enabled
// only when NO_UNWIND_TABLES == 1).
bool ShouldEnableBreakpadMicrodumps() override { return true; }
int GetAndroidMinidumpDescriptor() override { return dump_fd_; }
int GetAndroidCrashSignalFD() override { return crash_signal_fd_; }
bool DumpWithoutCrashingToFd(int fd) {
// TODO(tobiasjs): figure out what to do with on demand minidump on the
// renderer process of webview.
breakpad::GenerateMinidumpOnDemandForAndroid(fd);
return true;
}
bool GetCrashDumpLocation(base::FilePath* crash_dir) override {
return base::PathService::Get(android_webview::DIR_CRASH_DUMPS, crash_dir);
}
void GetSanitizationInformation(const char* const** annotations_whitelist,
void** target_module,
bool* sanitize_stacks) override {
*annotations_whitelist = crash_keys::kWebViewCrashKeyWhiteList;
#if defined(COMPONENT_BUILD)
*target_module = nullptr;
#else
*target_module = reinterpret_cast<void*>(&EnableCrashReporter);
#endif
*sanitize_stacks = true;
}
unsigned int GetCrashDumpPercentageForWebView() override { return 100; }
private:
int dump_fd_;
int crash_signal_fd_;
bool in_crash_reporting_sample_;
DISALLOW_COPY_AND_ASSIGN(AwCrashReporterClient);
};
const char* const* AwCrashReporterClient::GetCrashKeyWhiteList() {
return crash_keys::kWebViewCrashKeyWhiteList;
}
bool AwCrashReporterClient::GetCollectStatsConsent() {
#if defined(GOOGLE_CHROME_BUILD)
// TODO(gsennton): Enabling minidump-generation unconditionally means we
// will generate minidumps even if the user doesn't consent to minidump
// uploads. However, we will check user-consent before uploading any
// minidumps, if we do not have user consent we will delete the minidumps.
// We should investigate whether we can avoid generating minidumps
// altogether if we don't have user consent, see crbug.com/692485
return in_crash_reporting_sample_;
#else
return false;
#endif // !defined(GOOGLE_CHROME_BUILD)
}
base::LazyInstance<AwCrashReporterClient>::Leaky g_crash_reporter_client =
LAZY_INSTANCE_INITIALIZER;
bool g_enabled = false;
#if defined(ARCH_CPU_X86_FAMILY)
bool SafeToUseSignalHandler() {
// N+ shared library namespacing means that we are unable to dlopen
// libnativebridge (because it isn't in the NDK). However we know
// that, were we able to, the tests below would pass, so just return
// true here.
if (base::android::BuildInfo::GetInstance()->sdk_int() >=
base::android::SDK_VERSION_NOUGAT) {
return true;
}
// On X86/64 there are binary translators that handle SIGSEGV in userspace and
// may get chained after our handler - see http://crbug.com/477444
// We attempt to detect this to work out when it's safe to install breakpad.
// If anything doesn't seem right we assume it's not safe.
// type and mangled name of android::NativeBridgeInitialized
typedef bool (*InitializedFunc)();
const char kInitializedSymbol[] = "_ZN7android23NativeBridgeInitializedEv";
// type and mangled name of android::NativeBridgeGetVersion
typedef uint32_t (*VersionFunc)();
const char kVersionSymbol[] = "_ZN7android22NativeBridgeGetVersionEv";
base::ScopedNativeLibrary lib_native_bridge(
base::FilePath("libnativebridge.so"));
if (!lib_native_bridge.is_valid()) {
DLOG(WARNING) << "Couldn't load libnativebridge";
return false;
}
InitializedFunc NativeBridgeInitialized = reinterpret_cast<InitializedFunc>(
lib_native_bridge.GetFunctionPointer(kInitializedSymbol));
if (NativeBridgeInitialized == nullptr) {
DLOG(WARNING) << "Couldn't tell if native bridge initialized";
return false;
}
if (!NativeBridgeInitialized()) {
// Native process, safe to use breakpad.
return true;
}
VersionFunc NativeBridgeGetVersion = reinterpret_cast<VersionFunc>(
lib_native_bridge.GetFunctionPointer(kVersionSymbol));
if (NativeBridgeGetVersion == nullptr) {
DLOG(WARNING) << "Couldn't get native bridge version";
return false;
}
uint32_t version = NativeBridgeGetVersion();
if (version >= 2) {
// Native bridge at least version 2, safe to use breakpad.
return true;
} else {
DLOG(WARNING) << "Native bridge ver=" << version << "; too low";
return false;
}
}
#endif
} // namespace
void EnableCrashReporter(const std::string& process_type, int crash_signal_fd) {
if (g_enabled) {
NOTREACHED() << "EnableCrashReporter called more than once";
return;
}
#if defined(ARCH_CPU_X86_FAMILY)
if (!SafeToUseSignalHandler()) {
LOG(WARNING) << "Can't use breakpad to handle WebView crashes";
return;
}
#endif
AwCrashReporterClient* client = g_crash_reporter_client.Pointer();
if (process_type == switches::kRendererProcess && crash_signal_fd != -1) {
client->set_crash_signal_fd(crash_signal_fd);
}
::crash_reporter::SetCrashReporterClient(client);
breakpad::SanitizationInfo sanitization_info;
sanitization_info.should_sanitize_dumps = true;
#if !defined(COMPONENT_BUILD)
sanitization_info.skip_dump_if_principal_mapping_not_referenced = true;
sanitization_info.address_within_principal_mapping =
reinterpret_cast<uintptr_t>(&EnableCrashReporter);
#endif // defined(COMPONENT_BUILD)
bool is_browser_process =
process_type.empty() ||
process_type == breakpad::kWebViewSingleProcessType ||
process_type == breakpad::kBrowserProcessType;
if (is_browser_process) {
breakpad::InitCrashReporter(process_type, sanitization_info);
} else {
breakpad::InitNonBrowserCrashReporterForAndroid(process_type,
sanitization_info);
}
g_enabled = true;
}
bool GetCrashDumpLocation(base::FilePath* crash_dir) {
return g_crash_reporter_client.Get().GetCrashDumpLocation(crash_dir);
}
void AddGpuFingerprintToMicrodumpCrashHandler(
const std::string& gpu_fingerprint) {
breakpad::AddGpuFingerprintToMicrodumpCrashHandler(gpu_fingerprint);
}
bool DumpWithoutCrashingToFd(int fd) {
::crash_reporter::SetCrashReporterClient(g_crash_reporter_client.Pointer());
return g_crash_reporter_client.Pointer()->DumpWithoutCrashingToFd(fd);
}
bool IsCrashReporterEnabled() {
return breakpad::IsCrashReporterEnabled();
}
void SuppressDumpGeneration() {
breakpad::SuppressDumpGeneration();
}
} // namespace crash_reporter
} // namespace android_webview