| // 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/crash_reporter/aw_microdump_crash_reporter.h" |
| |
| #include "android_webview/common/aw_version_info_values.h" |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/files/file_path.h" |
| #include "base/lazy_instance.h" |
| #include "base/scoped_native_library.h" |
| #include "base/synchronization/lock.h" |
| #include "build/build_config.h" |
| #include "components/crash/content/app/breakpad_linux.h" |
| #include "components/crash/content/app/crash_reporter_client.h" |
| #include "content/public/common/content_switches.h" |
| |
| namespace android_webview { |
| namespace crash_reporter { |
| |
| namespace { |
| |
| class AwCrashReporterClient : public ::crash_reporter::CrashReporterClient { |
| public: |
| AwCrashReporterClient() : dump_fd_(-1) {} |
| |
| // Does not use lock, can only be called immediately after creation. |
| void set_crash_signal_fd(int fd) { dump_fd_ = fd; } |
| |
| // crash_reporter::CrashReporterClient implementation. |
| bool IsRunningUnattended() override { return false; } |
| bool GetCollectStatsConsent() override { return false; } |
| |
| void GetProductNameAndVersion(const char** product_name, |
| const char** version) override { |
| *product_name = "WebView"; |
| *version = PRODUCT_VERSION; |
| } |
| // 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_; } |
| |
| bool DumpWithoutCrashingToFd(int fd) { |
| DCHECK(dump_fd_ == -1); |
| base::AutoLock lock(dump_lock_); |
| dump_fd_ = fd; |
| base::debug::DumpWithoutCrashing(); |
| dump_fd_ = -1; |
| return true; |
| } |
| |
| private: |
| int dump_fd_; |
| base::Lock dump_lock_; |
| DISALLOW_COPY_AND_ASSIGN(AwCrashReporterClient); |
| }; |
| |
| base::LazyInstance<AwCrashReporterClient>::Leaky g_crash_reporter_client = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| bool g_enabled = false; |
| |
| #if defined(ARCH_CPU_X86_FAMILY) |
| bool SafeToUseSignalHandler() { |
| // 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 EnableMicrodumpCrashReporter(const std::string& process_type, |
| int crash_signal_fd) { |
| if (g_enabled) { |
| NOTREACHED() << "EnableMicrodumpCrashReporter 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::InitMicrodumpCrashHandlerIfNecessary(process_type); |
| g_enabled = true; |
| } |
| |
| void AddGpuFingerprintToMicrodumpCrashHandler( |
| const std::string& gpu_fingerprint) { |
| breakpad::AddGpuFingerprintToMicrodumpCrashHandler(gpu_fingerprint); |
| } |
| |
| bool DumpWithoutCrashingToFd(int fd) { |
| return g_crash_reporter_client.Pointer()->DumpWithoutCrashingToFd(fd); |
| } |
| |
| } // namespace crash_reporter |
| } // namespace android_webview |