summaryrefslogtreecommitdiffstats
path: root/android_webview/crash_reporter/aw_microdump_crash_reporter.cc
blob: 165c4b313f12046ef25f37858e692220ecfb29a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// 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"

namespace android_webview {
namespace crash_reporter {

namespace {

class AwCrashReporterClient : public ::crash_reporter::CrashReporterClient {
 public:
  AwCrashReporterClient() : dump_fd_(-1) {}

  // 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) {
    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() {
  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

  ::crash_reporter::SetCrashReporterClient(g_crash_reporter_client.Pointer());

  // |process_type| is not really relevant here, as long as it not empty.
  breakpad::InitNonBrowserCrashReporterForAndroid("webview" /* 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