summaryrefslogtreecommitdiff
path: root/chromium/components/crash
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 15:28:34 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 13:54:51 +0000
commit2a19c63448c84c1805fb1a585c3651318bb86ca7 (patch)
treeeb17888e8531aa6ee5e85721bd553b832a7e5156 /chromium/components/crash
parentb014812705fc80bff0a5c120dfcef88f349816dc (diff)
downloadqtwebengine-chromium-2a19c63448c84c1805fb1a585c3651318bb86ca7.tar.gz
BASELINE: Update Chromium to 69.0.3497.70
Change-Id: I2b7b56e4e7a8b26656930def0d4575dc32b900a0 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/crash')
-rw-r--r--chromium/components/crash/android/OWNERS4
-rw-r--r--chromium/components/crash/content/app/BUILD.gn1
-rw-r--r--chromium/components/crash/content/app/breakpad_linux.cc25
-rw-r--r--chromium/components/crash/content/app/crash_reporter_client.cc11
-rw-r--r--chromium/components/crash/content/app/crash_reporter_client.h16
-rw-r--r--chromium/components/crash/content/app/crashpad_linux.cc81
-rw-r--r--chromium/components/crash/content/browser/BUILD.gn7
-rw-r--r--chromium/components/crash/content/browser/child_exit_observer_android.cc (renamed from chromium/components/crash/content/browser/crash_dump_observer_android.cc)46
-rw-r--r--chromium/components/crash/content/browser/child_exit_observer_android.h (renamed from chromium/components/crash/content/browser/crash_dump_observer_android.h)39
-rw-r--r--chromium/components/crash/content/browser/child_process_crash_observer_android.cc12
-rw-r--r--chromium/components/crash/content/browser/child_process_crash_observer_android.h13
-rw-r--r--chromium/components/crash/content/browser/crash_dump_manager_android.cc215
-rw-r--r--chromium/components/crash/content/browser/crash_dump_manager_android.h76
-rw-r--r--chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc175
-rw-r--r--chromium/components/crash/content/browser/crash_handler_host_linux.cc2
-rw-r--r--chromium/components/crash/content/browser/crash_metrics_reporter_android.cc271
-rw-r--r--chromium/components/crash/content/browser/crash_metrics_reporter_android.h91
-rw-r--r--chromium/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc255
-rwxr-xr-xchromium/components/crash/content/tools/generate_breakpad_symbols.py70
19 files changed, 1020 insertions, 390 deletions
diff --git a/chromium/components/crash/android/OWNERS b/chromium/components/crash/android/OWNERS
index 9c568c2eec8..1c0302aaf81 100644
--- a/chromium/components/crash/android/OWNERS
+++ b/chromium/components/crash/android/OWNERS
@@ -1,7 +1,3 @@
-# Java readability and UMA
-mariakhomenko@chromium.org
-
-# Crash uploading mechanism
gsennton@chromium.org
isherman@chromium.org
diff --git a/chromium/components/crash/content/app/BUILD.gn b/chromium/components/crash/content/app/BUILD.gn
index 4d9d30aa891..d4fa4c3e6de 100644
--- a/chromium/components/crash/content/app/BUILD.gn
+++ b/chromium/components/crash/content/app/BUILD.gn
@@ -74,6 +74,7 @@ static_library("app") {
"//content/public/common:result_codes",
"//sandbox",
"//third_party/breakpad:client",
+ "//third_party/crashpad/crashpad/snapshot",
]
# Clang's -mstackrealign doesn't work well with
diff --git a/chromium/components/crash/content/app/breakpad_linux.cc b/chromium/components/crash/content/app/breakpad_linux.cc
index 76f15cb5b51..a7378204ffa 100644
--- a/chromium/components/crash/content/app/breakpad_linux.cc
+++ b/chromium/components/crash/content/app/breakpad_linux.cc
@@ -55,6 +55,7 @@
#include <sys/stat.h>
#include "base/android/build_info.h"
+#include "base/android/java_exception_reporter.h"
#include "base/android/path_utils.h"
#include "base/debug/leak_annotations.h"
#endif
@@ -109,6 +110,21 @@ uint32_t g_dumps_suppressed = 0;
char* g_process_type = nullptr;
ExceptionHandler* g_microdump = nullptr;
int g_signal_code_pipe_fd = -1;
+char* g_java_exception_info = nullptr;
+
+void SetJavaExceptionInfo(const char* exception) {
+ if (g_java_exception_info) {
+ // The old exception should be cleared before setting a new one.
+ DCHECK(!exception);
+ free(g_java_exception_info);
+ }
+
+ if (exception) {
+ g_java_exception_info = strndup(exception, 5 * 4096);
+ } else {
+ g_java_exception_info = nullptr;
+ }
+}
class MicrodumpInfo {
public:
@@ -1745,9 +1761,8 @@ void HandleCrashDump(const BreakpadInfo& info) {
WriteAndroidPackage(writer, android_build_info);
writer.AddBoundary();
}
- if (android_build_info->java_exception_info() != nullptr) {
- writer.AddPairString(exception_info,
- android_build_info->java_exception_info());
+ if (g_java_exception_info != nullptr) {
+ writer.AddPairString(exception_info, g_java_exception_info);
writer.AddBoundary();
}
#endif
@@ -1942,6 +1957,8 @@ void InitCrashReporter(const std::string& process_type,
void InitCrashReporter(const std::string& process_type) {
#endif // defined(OS_ANDROID)
#if defined(OS_ANDROID)
+ base::android::SetJavaExceptionCallback(SetJavaExceptionInfo);
+
// This will guarantee that the BuildInfo has been initialized and subsequent
// calls will not require memory allocation.
base::android::BuildInfo::GetInstance();
@@ -2029,6 +2046,8 @@ void InitNonBrowserCrashReporterForAndroid(
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
+ base::android::SetJavaExceptionCallback(SetJavaExceptionInfo);
+
// Handler registration is LIFO. Install the microdump handler first, such
// that if conventional minidump crash reporting is enabled below, it takes
// precedence (i.e. its handler is run first) over the microdump handler.
diff --git a/chromium/components/crash/content/app/crash_reporter_client.cc b/chromium/components/crash/content/app/crash_reporter_client.cc
index 464fe2efc0d..f69c6c3ff26 100644
--- a/chromium/components/crash/content/app/crash_reporter_client.cc
+++ b/chromium/components/crash/content/app/crash_reporter_client.cc
@@ -171,6 +171,17 @@ bool CrashReporterClient::ShouldEnableBreakpadMicrodumps() {
}
#endif
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+void CrashReporterClient::GetSanitizationInformation(
+ const char* const** annotations_whitelist,
+ void** target_module,
+ bool* sanitize_stacks) {
+ *annotations_whitelist = nullptr;
+ *target_module = nullptr;
+ *sanitize_stacks = false;
+}
+#endif
+
bool CrashReporterClient::ShouldMonitorCrashHandlerExpensively() {
return false;
}
diff --git a/chromium/components/crash/content/app/crash_reporter_client.h b/chromium/components/crash/content/app/crash_reporter_client.h
index e6a09126bdb..485c2c8bf63 100644
--- a/chromium/components/crash/content/app/crash_reporter_client.h
+++ b/chromium/components/crash/content/app/crash_reporter_client.h
@@ -165,6 +165,22 @@ class CrashReporterClient {
virtual bool ShouldEnableBreakpadMicrodumps();
#endif
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+ // Configures sanitization of crash dumps.
+ // |annotations_whitelist| is a nullptr terminated array of NUL-terminated
+ // strings of allowed annotation names or nullptr if all annotations are
+ // allowed. |target_module| is a pointer to a location inside a module to
+ // target or nullptr if there is no target module. Crash dumps are not
+ // produced when the crashing thread's stack and program counter do not
+ // reference the target module. |sanitize_stacks| is true if stacks should be
+ // sanitized for possible PII. If they are sanitized, only small integers and
+ // pointers to modules and stacks will be preserved.
+ virtual void GetSanitizationInformation(
+ const char* const** annotations_whitelist,
+ void** target_module,
+ bool* sanitize_stacks);
+#endif
+
// This method should return true to configure a crash reporter capable of
// monitoring itself for its own crashes to do so, even if self-monitoring
// would be expensive. "Expensive" self-monitoring dedicates an additional
diff --git a/chromium/components/crash/content/app/crashpad_linux.cc b/chromium/components/crash/content/app/crashpad_linux.cc
index d494877ca88..c8f87a85607 100644
--- a/chromium/components/crash/content/app/crashpad_linux.cc
+++ b/chromium/components/crash/content/app/crashpad_linux.cc
@@ -16,22 +16,43 @@
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/global_descriptors.h"
+#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "components/crash/content/app/crash_reporter_client.h"
#include "content/public/common/content_descriptors.h"
#include "sandbox/linux/services/syscall_wrappers.h"
+#include "third_party/crashpad/crashpad/client/annotation.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#include "third_party/crashpad/crashpad/snapshot/sanitized/sanitization_information.h"
#include "third_party/crashpad/crashpad/util/linux/exception_handler_client.h"
#include "third_party/crashpad/crashpad/util/linux/exception_information.h"
#include "third_party/crashpad/crashpad/util/misc/from_pointer_cast.h"
#include "third_party/crashpad/crashpad/util/posix/signals.h"
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#include "base/android/java_exception_reporter.h"
+#endif // OS_ANDROID
+
namespace crashpad {
namespace {
+bool SetSanitizationInfo(SanitizationInformation* info) {
+ const char* const* whitelist = nullptr;
+ void* target_module = nullptr;
+ bool sanitize_stacks = false;
+ crash_reporter::GetCrashReporterClient()->GetSanitizationInformation(
+ &whitelist, &target_module, &sanitize_stacks);
+ info->annotations_whitelist_address = FromPointerCast<VMAddress>(whitelist);
+ info->target_module_address = FromPointerCast<VMAddress>(target_module);
+ info->sanitize_stacks = sanitize_stacks;
+ return whitelist != nullptr || target_module != nullptr || sanitize_stacks;
+}
+
// A signal handler for non-browser processes in the sandbox.
// Sends a message to a crashpad::CrashHandlerHost to handle the crash.
class SandboxedHandler {
@@ -42,6 +63,7 @@ class SandboxedHandler {
}
bool Initialize() {
+ SetSanitizationInfo(&sanitization_);
server_fd_ = base::GlobalDescriptors::GetInstance()->Get(
service_manager::kCrashDumpSignal);
@@ -107,6 +129,10 @@ class SandboxedHandler {
FromPointerCast<decltype(info.exception_information_address)>(
&exception_information);
+ info.sanitization_information_address =
+ FromPointerCast<decltype(info.sanitization_information_address)>(
+ &state->sanitization_);
+
ExceptionHandlerClient handler_client(connection.get());
handler_client.SetCanSetPtracer(false);
handler_client.RequestCrashDump(info);
@@ -115,6 +141,7 @@ class SandboxedHandler {
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
}
+ SanitizationInformation sanitization_;
int server_fd_;
DISALLOW_COPY_AND_ASSIGN(SandboxedHandler);
@@ -123,6 +150,45 @@ class SandboxedHandler {
} // namespace
} // namespace crashpad
+#if defined(OS_ANDROID)
+
+namespace {
+
+void SetJavaExceptionInfo(const char* info_string) {
+ static crashpad::StringAnnotation<5 * 4096> exception_info("exception_info");
+ if (info_string) {
+ exception_info.Set(info_string);
+ } else {
+ exception_info.Clear();
+ }
+}
+
+void SetBuildInfoAnnotations(std::map<std::string, std::string>* annotations) {
+ base::android::BuildInfo* info = base::android::BuildInfo::GetInstance();
+
+ (*annotations)["android_build_id"] = info->android_build_id();
+ (*annotations)["android_build_fp"] = info->android_build_fp();
+ (*annotations)["device"] = info->device();
+ (*annotations)["model"] = info->model();
+ (*annotations)["brand"] = info->brand();
+ (*annotations)["board"] = info->board();
+ (*annotations)["installer_package_name"] = info->installer_package_name();
+ (*annotations)["abi_name"] = info->abi_name();
+ (*annotations)["custom_themes"] = info->custom_themes();
+ (*annotations)["resources_verison"] = info->resources_version();
+ (*annotations)["gms_core_version"] = info->gms_version_code();
+
+ if (info->firebase_app_id()[0] != '\0') {
+ (*annotations)["package"] = std::string(info->firebase_app_id()) + " v" +
+ info->package_version_code() + " (" +
+ info->package_version_name() + ")";
+ }
+}
+
+} // namespace
+
+#endif // OS_ANDROID
+
namespace crash_reporter {
namespace internal {
@@ -196,6 +262,10 @@ bool BuildHandlerArgs(base::FilePath* handler_path,
(*process_annotations)["prod"] = product_name;
(*process_annotations)["ver"] = product_version;
+#if defined(OS_ANDROID)
+ SetBuildInfoAnnotations(process_annotations);
+#endif // OS_ANDROID
+
#if defined(GOOGLE_CHROME_BUILD)
// Empty means stable.
const bool allow_empty_channel = true;
@@ -234,6 +304,10 @@ base::FilePath PlatformCrashpadInitialization(bool initial_client,
DCHECK(!embedded_handler);
DCHECK(exe_path.empty());
+#if defined(OS_ANDROID)
+ base::android::SetJavaExceptionCallback(SetJavaExceptionInfo);
+#endif // OS_ANDROID
+
if (browser_process) {
base::FilePath handler_path;
base::FilePath database_path;
@@ -246,6 +320,13 @@ base::FilePath PlatformCrashpadInitialization(bool initial_client,
return base::FilePath();
}
+ static base::NoDestructor<crashpad::SanitizationInformation>
+ sanitization_info;
+ if (crashpad::SetSanitizationInfo(sanitization_info.get())) {
+ arguments.push_back(base::StringPrintf("--sanitization-information=%p",
+ sanitization_info.get()));
+ }
+
bool result = GetCrashpadClient().StartHandlerAtCrash(
handler_path, database_path, metrics_path, url, process_annotations,
arguments);
diff --git a/chromium/components/crash/content/browser/BUILD.gn b/chromium/components/crash/content/browser/BUILD.gn
index 64761ead918..68c98d4cf66 100644
--- a/chromium/components/crash/content/browser/BUILD.gn
+++ b/chromium/components/crash/content/browser/BUILD.gn
@@ -11,12 +11,14 @@ assert(!is_fuchsia)
source_set("browser") {
sources = [
+ "child_exit_observer_android.cc",
+ "child_exit_observer_android.h",
"child_process_crash_observer_android.cc",
"child_process_crash_observer_android.h",
"crash_dump_manager_android.cc",
"crash_dump_manager_android.h",
- "crash_dump_observer_android.cc",
- "crash_dump_observer_android.h",
+ "crash_metrics_reporter_android.cc",
+ "crash_metrics_reporter_android.h",
]
deps = [
@@ -57,6 +59,7 @@ source_set("unit_tests") {
testonly = true
sources = [
"crash_dump_manager_android_unittest.cc",
+ "crash_metrics_reporter_android_unittest.cc",
]
deps = [
":browser",
diff --git a/chromium/components/crash/content/browser/crash_dump_observer_android.cc b/chromium/components/crash/content/browser/child_exit_observer_android.cc
index 7e2883d9c39..aa028dfe36a 100644
--- a/chromium/components/crash/content/browser/crash_dump_observer_android.cc
+++ b/chromium/components/crash/content/browser/child_exit_observer_android.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/crash/content/browser/crash_dump_observer_android.h"
+#include "components/crash/content/browser/child_exit_observer_android.h"
#include <unistd.h>
@@ -18,48 +18,54 @@
using content::BrowserThread;
-namespace breakpad {
+namespace crash_reporter {
namespace {
-base::LazyInstance<CrashDumpObserver>::DestructorAtExit g_instance =
+base::LazyInstance<ChildExitObserver>::DestructorAtExit g_instance =
LAZY_INSTANCE_INITIALIZER;
void PopulateTerminationInfo(
const content::ChildProcessTerminationInfo& content_info,
- CrashDumpObserver::TerminationInfo* info) {
- info->has_oom_protection_bindings = content_info.has_oom_protection_bindings;
+ ChildExitObserver::TerminationInfo* info) {
+ info->binding_state = content_info.binding_state;
info->was_killed_intentionally_by_browser =
content_info.was_killed_intentionally_by_browser;
+ info->remaining_process_with_strong_binding =
+ content_info.remaining_process_with_strong_binding;
+ info->remaining_process_with_moderate_binding =
+ content_info.remaining_process_with_moderate_binding;
+ info->remaining_process_with_waived_binding =
+ content_info.remaining_process_with_waived_binding;
info->was_oom_protected_status =
content_info.status == base::TERMINATION_STATUS_OOM_PROTECTED;
}
} // namespace
-CrashDumpObserver::TerminationInfo::TerminationInfo() = default;
-CrashDumpObserver::TerminationInfo::TerminationInfo(
+ChildExitObserver::TerminationInfo::TerminationInfo() = default;
+ChildExitObserver::TerminationInfo::TerminationInfo(
const TerminationInfo& other) = default;
-CrashDumpObserver::TerminationInfo& CrashDumpObserver::TerminationInfo::
+ChildExitObserver::TerminationInfo& ChildExitObserver::TerminationInfo::
operator=(const TerminationInfo& other) = default;
// static
-void CrashDumpObserver::Create() {
+void ChildExitObserver::Create() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// If this DCHECK fails in a unit test then a previously executing
- // test that makes use of CrashDumpObserver forgot to create a
+ // test that makes use of ChildExitObserver forgot to create a
// ShadowingAtExitManager.
DCHECK(!g_instance.IsCreated());
g_instance.Get();
}
// static
-CrashDumpObserver* CrashDumpObserver::GetInstance() {
+ChildExitObserver* ChildExitObserver::GetInstance() {
DCHECK(g_instance.IsCreated());
return g_instance.Pointer();
}
-CrashDumpObserver::CrashDumpObserver() {
+ChildExitObserver::ChildExitObserver() {
notification_registrar_.Add(this,
content::NOTIFICATION_RENDERER_PROCESS_CREATED,
content::NotificationService::AllSources());
@@ -72,17 +78,17 @@ CrashDumpObserver::CrashDumpObserver() {
BrowserChildProcessObserver::Add(this);
}
-CrashDumpObserver::~CrashDumpObserver() {
+ChildExitObserver::~ChildExitObserver() {
BrowserChildProcessObserver::Remove(this);
}
-void CrashDumpObserver::RegisterClient(std::unique_ptr<Client> client) {
+void ChildExitObserver::RegisterClient(std::unique_ptr<Client> client) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::AutoLock auto_lock(registered_clients_lock_);
registered_clients_.push_back(std::move(client));
}
-void CrashDumpObserver::OnChildExit(const TerminationInfo& info) {
+void ChildExitObserver::OnChildExit(const TerminationInfo& info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::vector<Client*> registered_clients_copy;
{
@@ -95,7 +101,7 @@ void CrashDumpObserver::OnChildExit(const TerminationInfo& info) {
}
}
-void CrashDumpObserver::BrowserChildProcessStarted(
+void ChildExitObserver::BrowserChildProcessStarted(
int process_host_id,
content::PosixFileDescriptorInfo* mappings) {
std::vector<Client*> registered_clients_copy;
@@ -109,7 +115,7 @@ void CrashDumpObserver::BrowserChildProcessStarted(
}
}
-void CrashDumpObserver::BrowserChildProcessHostDisconnected(
+void ChildExitObserver::BrowserChildProcessHostDisconnected(
const content::ChildProcessData& data) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TerminationInfo info;
@@ -127,7 +133,7 @@ void CrashDumpObserver::BrowserChildProcessHostDisconnected(
OnChildExit(info);
}
-void CrashDumpObserver::BrowserChildProcessKilled(
+void ChildExitObserver::BrowserChildProcessKilled(
const content::ChildProcessData& data,
const content::ChildProcessTerminationInfo& content_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -142,7 +148,7 @@ void CrashDumpObserver::BrowserChildProcessKilled(
// Subsequent BrowserChildProcessHostDisconnected will call OnChildExit.
}
-void CrashDumpObserver::Observe(int type,
+void ChildExitObserver::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -193,4 +199,4 @@ void CrashDumpObserver::Observe(int type,
OnChildExit(info);
}
-} // namespace breakpad
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/browser/crash_dump_observer_android.h b/chromium/components/crash/content/browser/child_exit_observer_android.h
index 2564e1ecbae..5e9f239c309 100644
--- a/chromium/components/crash/content/browser/crash_dump_observer_android.h
+++ b/chromium/components/crash/content/browser/child_exit_observer_android.h
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_OBSERVER_ANDROID_H_
-#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_OBSERVER_ANDROID_H_
+#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_EXIT_OBSERVER_ANDROID_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_EXIT_OBSERVER_ANDROID_H_
#include <memory>
#include <vector>
#include "base/android/application_status_listener.h"
+#include "base/android/child_process_binding_types.h"
#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
#include "base/process/process.h"
@@ -23,12 +24,12 @@ namespace content {
struct ChildProcessTerminationInfo;
}
-namespace breakpad {
+namespace crash_reporter {
// This class centralises the observation of child processes for the
// purpose of reacting to child process crashes.
-// The CrashDumpObserver instance exists on the browser main thread.
-class CrashDumpObserver : public content::BrowserChildProcessObserver,
+// The ChildExitObserver instance exists on the browser main thread.
+class ChildExitObserver : public content::BrowserChildProcessObserver,
public content::NotificationObserver {
public:
struct TerminationInfo {
@@ -49,8 +50,12 @@ class CrashDumpObserver : public content::BrowserChildProcessObserver,
// Values from ChildProcessTerminationInfo.
// Note base::TerminationStatus and exit_code are missing intentionally
// because those fields hold no useful information on Android.
- bool has_oom_protection_bindings = false;
+ base::android::ChildBindingState binding_state =
+ base::android::ChildBindingState::UNBOUND;
bool was_killed_intentionally_by_browser = false;
+ int remaining_process_with_strong_binding = 0;
+ int remaining_process_with_moderate_binding = 0;
+ int remaining_process_with_waived_binding = 0;
// Note this is slightly different |has_oom_protection_bindings|.
// This is equivalent to status == TERMINATION_STATUS_NORMAL_TERMINATION,
@@ -70,7 +75,7 @@ class CrashDumpObserver : public content::BrowserChildProcessObserver,
bool renderer_was_subframe = false;
};
- // CrashDumpObserver client interface.
+ // ChildExitObserver client interface.
// Client methods will be called synchronously in the order in which
// clients were registered. It is the implementer's responsibility
// to post tasks to the appropriate threads if required (and be
@@ -93,31 +98,31 @@ class CrashDumpObserver : public content::BrowserChildProcessObserver,
virtual ~Client() {}
};
- // The global CrashDumpObserver instance is created by calling
+ // The global ChildExitObserver instance is created by calling
// Create (on the UI thread), and lives until process exit. Tests
// making use of this class should register an AtExitManager.
static void Create();
- // Fetch a pointer to the global CrashDumpObserver instance. The
+ // Fetch a pointer to the global ChildExitObserver instance. The
// global instance must have been created by the time GetInstance is
// called.
- static CrashDumpObserver* GetInstance();
+ static ChildExitObserver* GetInstance();
void RegisterClient(std::unique_ptr<Client> client);
// BrowserChildProcessStarted must be called from
// ContentBrowserClient::GetAdditionalMappedFilesForChildProcess
- // overrides, to notify the CrashDumpObserver of child process
+ // overrides, to notify the ChildExitObserver of child process
// creation, and to allow clients to register any fd mappings they
// need.
void BrowserChildProcessStarted(int process_host_id,
content::PosixFileDescriptorInfo* mappings);
private:
- friend struct base::LazyInstanceTraitsBase<CrashDumpObserver>;
+ friend struct base::LazyInstanceTraitsBase<ChildExitObserver>;
- CrashDumpObserver();
- ~CrashDumpObserver() override;
+ ChildExitObserver();
+ ~ChildExitObserver() override;
// content::BrowserChildProcessObserver implementation:
void BrowserChildProcessHostDisconnected(
@@ -145,9 +150,9 @@ class CrashDumpObserver : public content::BrowserChildProcessObserver,
// Key is process_host_id. Only used for BrowserChildProcessHost.
std::map<int, TerminationInfo> browser_child_process_info_;
- DISALLOW_COPY_AND_ASSIGN(CrashDumpObserver);
+ DISALLOW_COPY_AND_ASSIGN(ChildExitObserver);
};
-} // namespace breakpad
+} // namespace crash_reporter
-#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_OBSERVER_ANDROID_H_
+#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_EXIT_OBSERVER_ANDROID_H_
diff --git a/chromium/components/crash/content/browser/child_process_crash_observer_android.cc b/chromium/components/crash/content/browser/child_process_crash_observer_android.cc
index 83d10f0c0d2..28aada7b761 100644
--- a/chromium/components/crash/content/browser/child_process_crash_observer_android.cc
+++ b/chromium/components/crash/content/browser/child_process_crash_observer_android.cc
@@ -12,7 +12,7 @@
#include "components/crash/content/app/breakpad_linux.h"
#include "components/crash/content/browser/crash_dump_manager_android.h"
-namespace breakpad {
+namespace crash_reporter {
ChildProcessCrashObserver::ChildProcessCrashObserver(
const base::FilePath crash_dump_dir,
@@ -28,23 +28,23 @@ void ChildProcessCrashObserver::OnChildStart(
return;
base::ScopedFD file(
- CrashDumpManager::GetInstance()->CreateMinidumpFileForChild(
+ breakpad::CrashDumpManager::GetInstance()->CreateMinidumpFileForChild(
process_host_id));
if (file.is_valid())
mappings->Transfer(descriptor_id_, std::move(file));
}
void ChildProcessCrashObserver::OnChildExit(
- const CrashDumpObserver::TerminationInfo& info) {
+ const ChildExitObserver::TerminationInfo& info) {
// This might be called twice for a given child process, with a
// NOTIFICATION_RENDERER_PROCESS_TERMINATED and then with
// NOTIFICATION_RENDERER_PROCESS_CLOSED.
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
- base::Bind(&CrashDumpManager::ProcessMinidumpFileFromChild,
- base::Unretained(CrashDumpManager::GetInstance()),
+ base::Bind(&breakpad::CrashDumpManager::ProcessMinidumpFileFromChild,
+ base::Unretained(breakpad::CrashDumpManager::GetInstance()),
crash_dump_dir_, info));
}
-} // namespace breakpad
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/browser/child_process_crash_observer_android.h b/chromium/components/crash/content/browser/child_process_crash_observer_android.h
index 07a119cee8e..7710e6f9d9b 100644
--- a/chromium/components/crash/content/browser/child_process_crash_observer_android.h
+++ b/chromium/components/crash/content/browser/child_process_crash_observer_android.h
@@ -6,20 +6,21 @@
#define COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_PROCESS_CRASH_OBSERVER_ANDROID_H_
#include "base/files/file_path.h"
-#include "components/crash/content/browser/crash_dump_observer_android.h"
+#include "components/crash/content/browser/child_exit_observer_android.h"
-namespace breakpad {
+namespace crash_reporter {
-class ChildProcessCrashObserver : public breakpad::CrashDumpObserver::Client {
+class ChildProcessCrashObserver
+ : public crash_reporter::ChildExitObserver::Client {
public:
ChildProcessCrashObserver(const base::FilePath crash_dump_dir,
int descriptor_id);
~ChildProcessCrashObserver() override;
- // breakpad::CrashDumpObserver::Client implementation:
+ // crash_reporter::ChildExitObserver::Client implementation:
void OnChildStart(int process_host_id,
content::PosixFileDescriptorInfo* mappings) override;
- void OnChildExit(const CrashDumpObserver::TerminationInfo& info) override;
+ void OnChildExit(const ChildExitObserver::TerminationInfo& info) override;
private:
base::FilePath crash_dump_dir_;
@@ -30,6 +31,6 @@ class ChildProcessCrashObserver : public breakpad::CrashDumpObserver::Client {
DISALLOW_COPY_AND_ASSIGN(ChildProcessCrashObserver);
};
-} // namespace breakpad
+} // namespace crash_reporter
#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_PROCESS_CRASH_OBSERVER_ANDROID_H_
diff --git a/chromium/components/crash/content/browser/crash_dump_manager_android.cc b/chromium/components/crash/content/browser/crash_dump_manager_android.cc
index 1cc00de0bb2..3e743de1503 100644
--- a/chromium/components/crash/content/browser/crash_dump_manager_android.cc
+++ b/chromium/components/crash/content/browser/crash_dump_manager_android.cc
@@ -22,133 +22,42 @@
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/content/browser/crash_metrics_reporter_android.h"
#include "jni/CrashDumpManager_jni.h"
namespace breakpad {
namespace {
+
base::LazyInstance<CrashDumpManager>::Leaky g_instance =
LAZY_INSTANCE_INITIALIZER;
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class ProcessedCrashCounts {
- kGpuForegroundOom = 0,
- kRendererForegroundVisibleOom = 1,
- kRendererForegroundIntentionalKill = 2,
- kRendererForegroundVisibleSubframeOom = 3,
- kRendererForegroundVisibleSubframeIntentionalKill = 4,
- kRendererForegroundVisibleCrash = 5,
- kRendererForegroundVisibleSubframeCrash = 6,
- kGpuCrashAll = 7,
- kRendererCrashAll = 8,
- kMaxValue = kRendererCrashAll
-};
-
-void LogCount(ProcessedCrashCounts type) {
- UMA_HISTOGRAM_ENUMERATION("Stability.Android.ProcessedCrashCounts", type);
-}
-
-void LogProcessedMetrics(const CrashDumpObserver::TerminationInfo& info,
- bool has_crash_dump) {
- const bool app_foreground =
- info.app_state ==
- base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
- info.app_state == base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES;
- const bool intentional_kill = info.was_killed_intentionally_by_browser;
- const bool android_oom_kill = !info.was_killed_intentionally_by_browser &&
- !has_crash_dump && !info.normal_termination;
- const bool renderer_visible = info.renderer_has_visible_clients;
- const bool renderer_sub_frame = info.renderer_was_subframe;
-
- if (info.process_type == content::PROCESS_TYPE_GPU) {
- if (app_foreground && android_oom_kill) {
- LogCount(ProcessedCrashCounts::kGpuForegroundOom);
- }
- if (has_crash_dump) {
- LogCount(ProcessedCrashCounts::kGpuCrashAll);
- }
- }
-
- if (info.process_type == content::PROCESS_TYPE_RENDERER) {
- if (app_foreground && renderer_visible) {
- if (android_oom_kill) {
- LogCount(
- renderer_sub_frame
- ? ProcessedCrashCounts::kRendererForegroundVisibleSubframeOom
- : ProcessedCrashCounts::kRendererForegroundVisibleOom);
- }
- if (has_crash_dump) {
- LogCount(
- renderer_sub_frame
- ? ProcessedCrashCounts::kRendererForegroundVisibleSubframeCrash
- : ProcessedCrashCounts::kRendererForegroundVisibleCrash);
- }
- }
- if (app_foreground && intentional_kill) {
- LogCount(ProcessedCrashCounts::kRendererForegroundIntentionalKill);
- if (renderer_visible && renderer_sub_frame) {
- LogCount(ProcessedCrashCounts::
- kRendererForegroundVisibleSubframeIntentionalKill);
- }
- }
- if (has_crash_dump) {
- LogCount(ProcessedCrashCounts::kRendererCrashAll);
- }
+class DefaultUploader : public CrashDumpManager::Uploader {
+ public:
+ DefaultUploader() = default;
+ ~DefaultUploader() override = default;
+
+ // CrashDumpManager::Uploader:
+ void TryToUploadCrashDump(const base::FilePath& crash_dump_path) override {
+ // Hop over to Java to attempt to attach the logcat to the crash. This may
+ // fail, which is ok -- if it does, the crash will still be uploaded on the
+ // next browser start.
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jstring> j_dump_path =
+ base::android::ConvertUTF8ToJavaString(env, crash_dump_path.value());
+ Java_CrashDumpManager_tryToUploadMinidump(env, j_dump_path);
}
-}
+};
} // namespace
-CrashDumpManager::CrashDumpDetails::CrashDumpDetails(
- int process_host_id,
- content::ProcessType process_type,
- bool was_oom_protected_status,
- base::android::ApplicationState app_state)
- : process_host_id(process_host_id),
- process_type(process_type),
- was_oom_protected_status(was_oom_protected_status),
- app_state(app_state) {}
-
-CrashDumpManager::CrashDumpDetails::CrashDumpDetails() {}
-CrashDumpManager::CrashDumpDetails::~CrashDumpDetails() {}
-
-CrashDumpManager::CrashDumpDetails::CrashDumpDetails(
- const CrashDumpManager::CrashDumpDetails& other)
- : CrashDumpDetails(other.process_host_id,
- other.process_type,
- other.was_oom_protected_status,
- other.app_state) {
- file_size = other.file_size;
- status = other.status;
-}
-
// static
CrashDumpManager* CrashDumpManager::GetInstance() {
return g_instance.Pointer();
}
-// static
-bool CrashDumpManager::IsForegroundOom(const CrashDumpDetails& details) {
- // If the crash is size 0, it is an OOM.
- return details.process_type == content::PROCESS_TYPE_RENDERER &&
- details.was_oom_protected_status && details.file_size == 0 &&
- details.app_state ==
- base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
-}
-
-void CrashDumpManager::AddObserver(Observer* observer) {
- async_observers_->AddObserver(observer);
-}
-
-void CrashDumpManager::RemoveObserver(Observer* observer) {
- async_observers_->RemoveObserver(observer);
-}
-
CrashDumpManager::CrashDumpManager()
- : async_observers_(
- base::MakeRefCounted<
- base::ObserverListThreadSafe<CrashDumpManager::Observer>>()) {}
+ : uploader_(std::make_unique<DefaultUploader>()) {}
CrashDumpManager::~CrashDumpManager() {}
@@ -177,88 +86,48 @@ base::ScopedFD CrashDumpManager::CreateMinidumpFileForChild(
void CrashDumpManager::ProcessMinidumpFileFromChild(
base::FilePath crash_dump_dir,
- const CrashDumpObserver::TerminationInfo& info) {
+ const crash_reporter::ChildExitObserver::TerminationInfo& info) {
+ CrashDumpManager::CrashDumpStatus status =
+ ProcessMinidumpFileFromChildInternal(crash_dump_dir, info);
+ crash_reporter::CrashMetricsReporter::GetInstance()->CrashDumpProcessed(
+ info, status);
+}
+
+CrashDumpManager::CrashDumpStatus
+CrashDumpManager::ProcessMinidumpFileFromChildInternal(
+ base::FilePath crash_dump_dir,
+ const crash_reporter::ChildExitObserver::TerminationInfo& info) {
base::AssertBlockingAllowed();
- CrashDumpDetails details(info.process_host_id, info.process_type,
- info.was_oom_protected_status, info.app_state);
base::FilePath minidump_path;
// If the minidump for a given child process has already been
// processed, then there is no more work to do.
if (!GetMinidumpPath(info.process_host_id, &minidump_path)) {
- NotifyObservers(details);
- return;
+ return CrashDumpStatus::kMissingDump;
}
int64_t file_size = 0;
if (!base::PathExists(minidump_path)) {
LOG(ERROR) << "minidump does not exist " << minidump_path.value();
- return;
+ return CrashDumpStatus::kMissingDump;
}
int r = base::GetFileSize(minidump_path, &file_size);
DCHECK(r) << "Failed to retrieve size for minidump " << minidump_path.value();
- // TODO(wnwen): If these numbers match up to TabWebContentsObserver's
- // TabRendererCrashStatus histogram, then remove that one as this is more
- // accurate with more detail.
- if ((info.process_type == content::PROCESS_TYPE_RENDERER ||
- info.process_type == content::PROCESS_TYPE_GPU) &&
- info.app_state != base::android::APPLICATION_STATE_UNKNOWN) {
- ExitStatus exit_status;
- bool is_running = (info.app_state ==
- base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
- bool is_paused = (info.app_state ==
- base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES);
- if (file_size == 0) {
- if (is_running) {
- exit_status = EMPTY_MINIDUMP_WHILE_RUNNING;
- } else if (is_paused) {
- exit_status = EMPTY_MINIDUMP_WHILE_PAUSED;
- } else {
- exit_status = EMPTY_MINIDUMP_WHILE_BACKGROUND;
- }
- } else {
- if (is_running) {
- exit_status = VALID_MINIDUMP_WHILE_RUNNING;
- } else if (is_paused) {
- exit_status = VALID_MINIDUMP_WHILE_PAUSED;
- } else {
- exit_status = VALID_MINIDUMP_WHILE_BACKGROUND;
- }
- }
- if (info.process_type == content::PROCESS_TYPE_RENDERER) {
- if (info.was_oom_protected_status) {
- UMA_HISTOGRAM_ENUMERATION("Tab.RendererDetailedExitStatus", exit_status,
- ExitStatus::MINIDUMP_STATUS_COUNT);
- } else {
- UMA_HISTOGRAM_ENUMERATION("Tab.RendererDetailedExitStatusUnbound",
- exit_status,
- ExitStatus::MINIDUMP_STATUS_COUNT);
- }
- } else if (info.process_type == content::PROCESS_TYPE_GPU) {
- UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessDetailedExitStatus", exit_status,
- ExitStatus::MINIDUMP_STATUS_COUNT);
- }
- }
-
- LogProcessedMetrics(info, file_size != 0);
-
if (file_size == 0) {
// Empty minidump, this process did not crash. Just remove the file.
r = base::DeleteFile(minidump_path, false);
DCHECK(r) << "Failed to delete temporary minidump file "
<< minidump_path.value();
- details.status = CrashDumpStatus::kEmptyDump;
- NotifyObservers(details);
- return;
+ return CrashDumpStatus::kEmptyDump;
}
// We are dealing with a valid minidump. Copy it to the crash report
// directory from where Java code will upload it later on.
if (crash_dump_dir.empty()) {
NOTREACHED() << "Failed to retrieve the crash dump directory.";
- return;
+ return CrashDumpStatus::kDumpProcessingFailed;
}
const uint64_t rand = base::RandUint64();
const std::string filename =
@@ -270,24 +139,12 @@ void CrashDumpManager::ProcessMinidumpFileFromChild(
LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value()
<< " to " << dest_path.value();
base::DeleteFile(minidump_path, false);
- return;
+ return CrashDumpStatus::kDumpProcessingFailed;
}
VLOG(1) << "Crash minidump successfully generated: " << dest_path.value();
- // Hop over to Java to attempt to attach the logcat to the crash. This may
- // fail, which is ok -- if it does, the crash will still be uploaded on the
- // next browser start.
- JNIEnv* env = base::android::AttachCurrentThread();
- base::android::ScopedJavaLocalRef<jstring> j_dest_path =
- base::android::ConvertUTF8ToJavaString(env, dest_path.value());
- Java_CrashDumpManager_tryToUploadMinidump(env, j_dest_path);
- details.status = CrashDumpStatus::kValidDump;
- NotifyObservers(details);
-}
-
-void CrashDumpManager::NotifyObservers(const CrashDumpDetails& details) {
- async_observers_->Notify(
- FROM_HERE, &CrashDumpManager::Observer::OnCrashDumpProcessed, details);
+ uploader_->TryToUploadCrashDump(dest_path);
+ return CrashDumpStatus::kValidDump;
}
void CrashDumpManager::SetMinidumpPath(int process_host_id,
diff --git a/chromium/components/crash/content/browser/crash_dump_manager_android.h b/chromium/components/crash/content/browser/crash_dump_manager_android.h
index 96f14e1b2c9..bc10af0c95e 100644
--- a/chromium/components/crash/content/browser/crash_dump_manager_android.h
+++ b/chromium/components/crash/content/browser/crash_dump_manager_android.h
@@ -6,15 +6,16 @@
#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_
#include <map>
+#include <memory>
+#include <utility>
#include "base/android/application_status_listener.h"
#include "base/files/file_path.h"
#include "base/files/platform_file.h"
#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
-#include "base/observer_list_threadsafe.h"
#include "base/synchronization/lock.h"
-#include "components/crash/content/browser/crash_dump_observer_android.h"
+#include "components/crash/content/browser/child_exit_observer_android.h"
#include "content/public/common/child_process_host.h"
#include "content/public/common/process_type.h"
@@ -32,78 +33,55 @@ namespace breakpad {
// terminates.
class CrashDumpManager {
public:
- // This enum is used to back a UMA histogram, and must be treated as
- // append-only.
- enum ExitStatus {
- EMPTY_MINIDUMP_WHILE_RUNNING,
- EMPTY_MINIDUMP_WHILE_PAUSED,
- EMPTY_MINIDUMP_WHILE_BACKGROUND,
- VALID_MINIDUMP_WHILE_RUNNING,
- VALID_MINIDUMP_WHILE_PAUSED,
- VALID_MINIDUMP_WHILE_BACKGROUND,
- MINIDUMP_STATUS_COUNT
- };
-
enum class CrashDumpStatus {
// The dump for this process did not have a path set. This can happen if the
// dump was already processed or if crash dump generation is not turned on.
- kNoDump,
+ kMissingDump,
// The crash dump was empty.
kEmptyDump,
+ // Minidump file was found, but could not be copied to crash dir.
+ kDumpProcessingFailed,
+
// The crash dump is valid.
kValidDump,
};
- struct CrashDumpDetails {
- CrashDumpDetails(int process_host_id,
- content::ProcessType process_type,
- bool was_oom_protected_status,
- base::android::ApplicationState app_state);
- CrashDumpDetails();
- ~CrashDumpDetails();
- CrashDumpDetails(const CrashDumpDetails& other);
-
- int process_host_id = content::ChildProcessHost::kInvalidUniqueID;
-
- content::ProcessType process_type = content::PROCESS_TYPE_UNKNOWN;
- bool was_oom_protected_status = false;
- base::android::ApplicationState app_state;
- int64_t file_size = 0;
- CrashDumpStatus status = CrashDumpStatus::kNoDump;
- };
- // Careful note: the CrashDumpManager observers are asynchronous, and are
- // notified via PostTask. This could be problematic with a large number of
- // observers. Consider using a middle-layer observer to fan out synchronously
- // to leaf observers if you need many objects listening to these messages.
- class Observer {
+ // Class which aids in uploading a crash dump.
+ class Uploader {
public:
- virtual void OnCrashDumpProcessed(const CrashDumpDetails& details) {}
+ virtual ~Uploader() = default;
+
+ // Attempts to upload the specified child process minidump.
+ virtual void TryToUploadCrashDump(
+ const base::FilePath& crash_dump_path) = 0;
};
static CrashDumpManager* GetInstance();
- // True when |details| is a foreground out of memory crash.
- static bool IsForegroundOom(const CrashDumpDetails& details);
-
- // Can be called on any thread.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
-
void ProcessMinidumpFileFromChild(
base::FilePath crash_dump_dir,
- const CrashDumpObserver::TerminationInfo& info);
+ const crash_reporter::ChildExitObserver::TerminationInfo& info);
base::ScopedFD CreateMinidumpFileForChild(int process_host_id);
+ // Careful, |uploader_| is accessed on one of the task scheduler threads.
+ // Tests should set this before any other threads are spawned.
+ void set_uploader_for_testing(std::unique_ptr<Uploader> uploader) {
+ DCHECK(uploader);
+ uploader_ = std::move(uploader);
+ }
+
private:
friend struct base::LazyInstanceTraitsBase<CrashDumpManager>;
CrashDumpManager();
~CrashDumpManager();
- void NotifyObservers(const CrashDumpDetails& details);
+ CrashDumpStatus ProcessMinidumpFileFromChildInternal(
+ base::FilePath crash_dump_dir,
+ const crash_reporter::ChildExitObserver::TerminationInfo& info);
typedef std::map<int, base::FilePath> ChildProcessIDToMinidumpPath;
@@ -111,8 +89,8 @@ class CrashDumpManager {
const base::FilePath& minidump_path);
bool GetMinidumpPath(int process_host_id, base::FilePath* minidump_path);
- scoped_refptr<base::ObserverListThreadSafe<CrashDumpManager::Observer>>
- async_observers_;
+ // Should never be nullptr.
+ std::unique_ptr<Uploader> uploader_;
// This map should only be accessed with its lock aquired as it is accessed
// from the PROCESS_LAUNCHER and UI threads.
diff --git a/chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc b/chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc
index a3ad2e198dc..83fd396f189 100644
--- a/chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc
+++ b/chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc
@@ -4,6 +4,7 @@
#include "components/crash/content/browser/crash_dump_manager_android.h"
+#include <string>
#include <utility>
#include "base/at_exit.h"
@@ -11,19 +12,63 @@
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/files/file.h"
+#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
#include "base/task_scheduler/post_task.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
+#include "components/crash/content/browser/crash_metrics_reporter_android.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace breakpad {
+class CrashDumpManagerTest;
+
+class CrashMetricsReporterObserver
+ : public crash_reporter::CrashMetricsReporter::Observer {
+ public:
+ CrashMetricsReporterObserver() {}
+ ~CrashMetricsReporterObserver() {}
+
+ // crash_reporter::CrashMetricsReporter::Observer:
+ void OnCrashDumpProcessed(
+ int rph_id,
+ const crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet&
+ reported_counts) override {
+ wait_run_loop_.QuitClosure().Run();
+ }
+
+ void WaitForProcessed() { wait_run_loop_.Run(); }
+
+ private:
+ base::RunLoop wait_run_loop_;
+ DISALLOW_COPY_AND_ASSIGN(CrashMetricsReporterObserver);
+};
+
+class NoOpUploader : public CrashDumpManager::Uploader {
+ public:
+ NoOpUploader(scoped_refptr<base::SequencedTaskRunner> test_runner,
+ CrashDumpManagerTest* test_harness)
+ : test_runner_(std::move(test_runner)), test_harness_(test_harness) {}
+ ~NoOpUploader() override = default;
+
+ // CrashDumpManager::Uploader:
+ void TryToUploadCrashDump(const base::FilePath& crash_dump_path) override;
+
+ private:
+ scoped_refptr<base::SequencedTaskRunner> test_runner_;
+ CrashDumpManagerTest* test_harness_;
+ DISALLOW_COPY_AND_ASSIGN(NoOpUploader);
+};
+
class CrashDumpManagerTest : public testing::Test {
public:
CrashDumpManagerTest()
@@ -34,130 +79,110 @@ class CrashDumpManagerTest : public testing::Test {
void SetUp() override {
// Initialize the manager.
ASSERT_TRUE(CrashDumpManager::GetInstance());
+ CrashDumpManager::GetInstance()->set_uploader_for_testing(
+ std::make_unique<NoOpUploader>(base::SequencedTaskRunnerHandle::Get(),
+ this));
}
- // TODO(csharrison): This test harness is not robust enough. Namely, it does
- // not support actually processing a non-empty crash dump, due to JNI calls.
- static void CreateAndProcessEmptyMinidump(
- const CrashDumpObserver::TerminationInfo& info) {
+ void OnUploadedCrashDump() {
+ dumps_uploaded_++;
+ if (!upload_waiter_.is_null())
+ std::move(upload_waiter_).Run();
+ }
+
+ void WaitForCrashDumpsUploaded(int num_dumps) {
+ EXPECT_LE(num_dumps, dumps_uploaded_);
+ while (num_dumps < dumps_uploaded_) {
+ base::RunLoop run_loop;
+ upload_waiter_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+ }
+
+ static void CreateAndProcessCrashDump(
+ const crash_reporter::ChildExitObserver::TerminationInfo& info,
+ const std::string& data) {
base::ScopedFD fd =
CrashDumpManager::GetInstance()->CreateMinidumpFileForChild(
info.process_host_id);
EXPECT_TRUE(fd.is_valid());
+ base::WriteFileDescriptor(fd.get(), data.data(), data.size());
base::ScopedTempDir dump_dir;
EXPECT_TRUE(dump_dir.CreateUniqueTempDir());
CrashDumpManager::GetInstance()->ProcessMinidumpFileFromChild(
dump_dir.GetPath(), info);
}
+ protected:
+ int dumps_uploaded_ = 0;
+
private:
base::ShadowingAtExitManager at_exit_;
base::test::ScopedTaskEnvironment scoped_environment_;
+ base::OnceClosure upload_waiter_;
DISALLOW_COPY_AND_ASSIGN(CrashDumpManagerTest);
};
-class CrashDumpManagerObserver : public CrashDumpManager::Observer {
- public:
- CrashDumpManagerObserver() {}
- ~CrashDumpManagerObserver() {}
-
- // CrashDumpManager::Observer:
- void OnCrashDumpProcessed(
- const CrashDumpManager::CrashDumpDetails& details) override {
- last_details_ = details;
- if (!wait_closure_.is_null())
- std::move(wait_closure_).Run();
- }
-
- const CrashDumpManager::CrashDumpDetails& last_details() const {
- return last_details_.value();
- }
-
- void WaitForProcessed() {
- base::RunLoop run_loop;
- wait_closure_ = run_loop.QuitClosure();
- run_loop.Run();
- }
-
- private:
- base::OnceClosure wait_closure_;
- base::Optional<CrashDumpManager::CrashDumpDetails> last_details_;
- DISALLOW_COPY_AND_ASSIGN(CrashDumpManagerObserver);
-};
+void NoOpUploader::TryToUploadCrashDump(const base::FilePath& crash_dump_path) {
+ test_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&CrashDumpManagerTest::OnUploadedCrashDump,
+ base::Unretained(test_harness_)));
+}
-TEST_F(CrashDumpManagerTest, SimpleOOM) {
+TEST_F(CrashDumpManagerTest, NoDumpCreated) {
base::HistogramTester histogram_tester;
CrashDumpManager* manager = CrashDumpManager::GetInstance();
- CrashDumpManagerObserver crash_dump_observer;
- manager->AddObserver(&crash_dump_observer);
+ CrashMetricsReporterObserver observer;
+ crash_reporter::CrashMetricsReporter::GetInstance()->AddObserver(&observer);
- CrashDumpObserver::TerminationInfo termination_info;
+ crash_reporter::ChildExitObserver::TerminationInfo termination_info;
termination_info.process_host_id = 1;
termination_info.pid = base::kNullProcessHandle;
termination_info.process_type = content::PROCESS_TYPE_RENDERER;
termination_info.app_state =
base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
termination_info.normal_termination = false;
- termination_info.has_oom_protection_bindings = true;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
termination_info.was_killed_intentionally_by_browser = false;
termination_info.was_oom_protected_status = true;
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
- base::Bind(&CrashDumpManagerTest::CreateAndProcessEmptyMinidump,
- termination_info));
- crash_dump_observer.WaitForProcessed();
-
- const CrashDumpManager::CrashDumpDetails& details =
- crash_dump_observer.last_details();
- EXPECT_EQ(termination_info.process_host_id, details.process_host_id);
- EXPECT_EQ(content::PROCESS_TYPE_RENDERER, details.process_type);
- EXPECT_TRUE(details.was_oom_protected_status);
- EXPECT_EQ(base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES,
- details.app_state);
- EXPECT_EQ(0, details.file_size);
- EXPECT_EQ(CrashDumpManager::CrashDumpStatus::kEmptyDump, details.status);
+ base::BindOnce(&CrashDumpManager::ProcessMinidumpFileFromChild,
+ base::Unretained(manager), base::FilePath(),
+ termination_info));
+ observer.WaitForProcessed();
- histogram_tester.ExpectUniqueSample(
- "Tab.RendererDetailedExitStatus",
- CrashDumpManager::EMPTY_MINIDUMP_WHILE_RUNNING, 1);
+ histogram_tester.ExpectTotalCount("Tab.RendererDetailedExitStatus", 0);
+ EXPECT_EQ(0, dumps_uploaded_);
}
-TEST_F(CrashDumpManagerTest, NoDumpCreated) {
+TEST_F(CrashDumpManagerTest, NonOomCrash) {
base::HistogramTester histogram_tester;
- CrashDumpManager* manager = CrashDumpManager::GetInstance();
- CrashDumpManagerObserver crash_dump_observer;
- manager->AddObserver(&crash_dump_observer);
+ CrashMetricsReporterObserver observer;
+ crash_reporter::CrashMetricsReporter::GetInstance()->AddObserver(&observer);
- CrashDumpObserver::TerminationInfo termination_info;
+ crash_reporter::ChildExitObserver::TerminationInfo termination_info;
termination_info.process_host_id = 1;
termination_info.pid = base::kNullProcessHandle;
termination_info.process_type = content::PROCESS_TYPE_RENDERER;
termination_info.app_state =
base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
termination_info.normal_termination = false;
- termination_info.has_oom_protection_bindings = true;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
termination_info.was_killed_intentionally_by_browser = false;
termination_info.was_oom_protected_status = true;
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
- base::Bind(&CrashDumpManager::ProcessMinidumpFileFromChild,
- base::Unretained(manager), base::FilePath(),
- termination_info));
- crash_dump_observer.WaitForProcessed();
-
- const CrashDumpManager::CrashDumpDetails& details =
- crash_dump_observer.last_details();
- EXPECT_EQ(termination_info.process_host_id, details.process_host_id);
- EXPECT_EQ(content::PROCESS_TYPE_RENDERER, details.process_type);
- EXPECT_TRUE(details.was_oom_protected_status);
- EXPECT_EQ(base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES,
- details.app_state);
- EXPECT_EQ(0, details.file_size);
- EXPECT_EQ(CrashDumpManager::CrashDumpStatus::kNoDump, details.status);
+ base::BindOnce(&CrashDumpManagerTest::CreateAndProcessCrashDump,
+ termination_info, "Some non-empty crash data"));
+ observer.WaitForProcessed();
- histogram_tester.ExpectTotalCount("Tab.RendererDetailedExitStatus", 0);
+ histogram_tester.ExpectUniqueSample(
+ "Tab.RendererDetailedExitStatus",
+ crash_reporter::CrashMetricsReporter::VALID_MINIDUMP_WHILE_RUNNING, 1);
+ WaitForCrashDumpsUploaded(1);
}
} // namespace breakpad
diff --git a/chromium/components/crash/content/browser/crash_handler_host_linux.cc b/chromium/components/crash/content/browser/crash_handler_host_linux.cc
index 57f710ab886..e672add8df1 100644
--- a/chromium/components/crash/content/browser/crash_handler_host_linux.cc
+++ b/chromium/components/crash/content/browser/crash_handler_host_linux.cc
@@ -48,7 +48,7 @@
#if !defined(OS_CHROMEOS)
#include "components/crash/content/app/crashpad.h"
-#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h" // nogncheck
#endif
using content::BrowserThread;
diff --git a/chromium/components/crash/content/browser/crash_metrics_reporter_android.cc b/chromium/components/crash/content/browser/crash_metrics_reporter_android.cc
new file mode 100644
index 00000000000..3c2babbd6b6
--- /dev/null
+++ b/chromium/components/crash/content/browser/crash_metrics_reporter_android.cc
@@ -0,0 +1,271 @@
+// Copyright 2018 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 "components/crash/content/browser/crash_metrics_reporter_android.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/optional.h"
+#include "components/crash/content/browser/crash_dump_manager_android.h"
+
+namespace crash_reporter {
+namespace {
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class BindingStateCombo {
+ kNoWaivedNoModerateNoStrong = 0,
+ kNoWaivedNoModerateHasStrong = 1,
+ kNoWaivedHasModerateNoStrong = 2,
+ kNoWaivedHasModerateHasStrong = 3,
+ kHasWaivedNoModerateNoStrong = 4,
+ kHasWaivedNoModerateHasStrong = 5,
+ kHasWaivedHasModerateNoStrong = 6,
+ kHasWaivedHasModerateHasStrong = 7,
+ kMaxValue = kHasWaivedHasModerateHasStrong
+};
+
+void ReportCrashCount(CrashMetricsReporter::ProcessedCrashCounts crash_type,
+ CrashMetricsReporter::ReportedCrashTypeSet* counts) {
+ UMA_HISTOGRAM_ENUMERATION("Stability.Android.ProcessedCrashCounts",
+ crash_type);
+ counts->insert(crash_type);
+}
+
+void ReportLegacyCrashUma(const ChildExitObserver::TerminationInfo& info,
+ bool has_valid_dump) {
+ // TODO(wnwen): If these numbers match up to TabWebContentsObserver's
+ // TabRendererCrashStatus histogram, then remove that one as this is more
+ // accurate with more detail.
+ if ((info.process_type == content::PROCESS_TYPE_RENDERER ||
+ info.process_type == content::PROCESS_TYPE_GPU) &&
+ info.app_state != base::android::APPLICATION_STATE_UNKNOWN) {
+ CrashMetricsReporter::ExitStatus exit_status;
+ bool is_running = (info.app_state ==
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
+ bool is_paused = (info.app_state ==
+ base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES);
+ if (!has_valid_dump) {
+ if (is_running) {
+ exit_status = CrashMetricsReporter::EMPTY_MINIDUMP_WHILE_RUNNING;
+ } else if (is_paused) {
+ exit_status = CrashMetricsReporter::EMPTY_MINIDUMP_WHILE_PAUSED;
+ } else {
+ exit_status = CrashMetricsReporter::EMPTY_MINIDUMP_WHILE_BACKGROUND;
+ }
+ } else {
+ if (is_running) {
+ exit_status = CrashMetricsReporter::VALID_MINIDUMP_WHILE_RUNNING;
+ } else if (is_paused) {
+ exit_status = CrashMetricsReporter::VALID_MINIDUMP_WHILE_PAUSED;
+ } else {
+ exit_status = CrashMetricsReporter::VALID_MINIDUMP_WHILE_BACKGROUND;
+ }
+ }
+ if (info.process_type == content::PROCESS_TYPE_RENDERER) {
+ if (info.was_oom_protected_status) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Tab.RendererDetailedExitStatus", exit_status,
+ CrashMetricsReporter::ExitStatus::MINIDUMP_STATUS_COUNT);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Tab.RendererDetailedExitStatusUnbound", exit_status,
+ CrashMetricsReporter::ExitStatus::MINIDUMP_STATUS_COUNT);
+ }
+ } else if (info.process_type == content::PROCESS_TYPE_GPU) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "GPU.GPUProcessDetailedExitStatus", exit_status,
+ CrashMetricsReporter::ExitStatus::MINIDUMP_STATUS_COUNT);
+ }
+ }
+}
+
+} // namespace
+
+// static
+CrashMetricsReporter* CrashMetricsReporter::GetInstance() {
+ static CrashMetricsReporter* instance = new CrashMetricsReporter();
+ return instance;
+}
+
+CrashMetricsReporter::CrashMetricsReporter()
+ : async_observers_(
+ base::MakeRefCounted<
+ base::ObserverListThreadSafe<CrashMetricsReporter::Observer>>()) {
+}
+
+CrashMetricsReporter::~CrashMetricsReporter() {}
+
+void CrashMetricsReporter::AddObserver(
+ CrashMetricsReporter::Observer* observer) {
+ async_observers_->AddObserver(observer);
+}
+
+void CrashMetricsReporter::RemoveObserver(
+ CrashMetricsReporter::Observer* observer) {
+ async_observers_->RemoveObserver(observer);
+}
+
+void CrashMetricsReporter::CrashDumpProcessed(
+ const ChildExitObserver::TerminationInfo& info,
+ breakpad::CrashDumpManager::CrashDumpStatus status) {
+ ReportedCrashTypeSet reported_counts;
+ if (status == breakpad::CrashDumpManager::CrashDumpStatus::kMissingDump) {
+ NotifyObservers(info.process_host_id, reported_counts);
+ return;
+ }
+
+ bool has_valid_dump = false;
+ switch (status) {
+ case breakpad::CrashDumpManager::CrashDumpStatus::kMissingDump:
+ NOTREACHED();
+ break;
+ case breakpad::CrashDumpManager::CrashDumpStatus::kEmptyDump:
+ has_valid_dump = false;
+ break;
+ case breakpad::CrashDumpManager::CrashDumpStatus::kValidDump:
+ case breakpad::CrashDumpManager::CrashDumpStatus::kDumpProcessingFailed:
+ has_valid_dump = true;
+ break;
+ }
+ const bool app_foreground =
+ info.app_state ==
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
+ info.app_state == base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES;
+ const bool intentional_kill = info.was_killed_intentionally_by_browser;
+ const bool android_oom_kill = !info.was_killed_intentionally_by_browser &&
+ !has_valid_dump && !info.normal_termination;
+ const bool renderer_visible = info.renderer_has_visible_clients;
+ const bool renderer_subframe = info.renderer_was_subframe;
+
+ if (info.process_type == content::PROCESS_TYPE_GPU && app_foreground &&
+ android_oom_kill) {
+ ReportCrashCount(ProcessedCrashCounts::kGpuForegroundOom, &reported_counts);
+ }
+
+ if (info.process_type == content::PROCESS_TYPE_RENDERER && app_foreground) {
+ if (renderer_visible) {
+ if (has_valid_dump) {
+ ReportCrashCount(
+ renderer_subframe
+ ? ProcessedCrashCounts::kRendererForegroundVisibleSubframeCrash
+ : ProcessedCrashCounts::kRendererForegroundVisibleCrash,
+ &reported_counts);
+ } else if (intentional_kill) {
+ ReportCrashCount(
+ renderer_subframe
+ ? ProcessedCrashCounts::
+ kRendererForegroundVisibleSubframeIntentionalKill
+ : ProcessedCrashCounts::
+ kRendererForegroundVisibleMainFrameIntentionalKill,
+ &reported_counts);
+ } else if (info.normal_termination) {
+ ReportCrashCount(ProcessedCrashCounts::
+ kRendererForegroundVisibleNormalTermNoMinidump,
+ &reported_counts);
+ } else {
+ DCHECK(android_oom_kill);
+ if (renderer_subframe) {
+ ReportCrashCount(
+ ProcessedCrashCounts::kRendererForegroundVisibleSubframeOom,
+ &reported_counts);
+ } else {
+ ReportCrashCount(ProcessedCrashCounts::kRendererForegroundVisibleOom,
+ &reported_counts);
+ base::RecordAction(
+ base::UserMetricsAction("RendererForegroundMainFrameOOM"));
+ }
+ }
+ } else if (!has_valid_dump) {
+ // Record stats when renderer is not visible, but the process has oom
+ // protected bindings. This case occurs when a tab is switched or closed,
+ // the bindings are updated later than visibility on web contents.
+ switch (info.binding_state) {
+ case base::android::ChildBindingState::UNBOUND:
+ case base::android::ChildBindingState::WAIVED:
+ break;
+ case base::android::ChildBindingState::STRONG:
+ if (intentional_kill || info.normal_termination) {
+ ReportCrashCount(
+ ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithStrongBindingKilled,
+ &reported_counts);
+ } else {
+ ReportCrashCount(
+ ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithStrongBindingOom,
+ &reported_counts);
+ }
+ break;
+ case base::android::ChildBindingState::MODERATE:
+ if (intentional_kill || info.normal_termination) {
+ ReportCrashCount(
+ ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithModerateBindingKilled,
+ &reported_counts);
+ } else {
+ ReportCrashCount(
+ ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithModerateBindingOom,
+ &reported_counts);
+ }
+ break;
+ }
+ }
+ }
+
+ if (info.process_type == content::PROCESS_TYPE_RENDERER &&
+ (info.app_state ==
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
+ info.app_state ==
+ base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES) &&
+ info.was_killed_intentionally_by_browser) {
+ ReportCrashCount(ProcessedCrashCounts::kRendererForegroundIntentionalKill,
+ &reported_counts);
+ }
+
+ if (has_valid_dump) {
+ ReportCrashCount(info.process_type == content::PROCESS_TYPE_GPU
+ ? ProcessedCrashCounts::kGpuCrashAll
+ : ProcessedCrashCounts::kRendererCrashAll,
+ &reported_counts);
+ }
+
+ if (app_foreground && android_oom_kill &&
+ info.binding_state == base::android::ChildBindingState::STRONG) {
+ const bool has_waived = info.remaining_process_with_waived_binding > 0;
+ const bool has_moderate = info.remaining_process_with_moderate_binding > 0;
+ const bool has_strong = info.remaining_process_with_strong_binding > 0;
+ BindingStateCombo combo;
+ if (has_waived && has_moderate) {
+ combo = has_strong ? BindingStateCombo::kHasWaivedHasModerateHasStrong
+ : BindingStateCombo::kHasWaivedHasModerateNoStrong;
+ } else if (has_waived) {
+ combo = has_strong ? BindingStateCombo::kHasWaivedNoModerateHasStrong
+ : BindingStateCombo::kHasWaivedNoModerateNoStrong;
+ } else if (has_moderate) {
+ combo = has_strong ? BindingStateCombo::kNoWaivedHasModerateHasStrong
+ : BindingStateCombo::kNoWaivedHasModerateNoStrong;
+ } else {
+ combo = has_strong ? BindingStateCombo::kNoWaivedNoModerateHasStrong
+ : BindingStateCombo::kNoWaivedNoModerateNoStrong;
+ }
+ UMA_HISTOGRAM_ENUMERATION(
+ "Stability.Android.StrongBindingOomRemainingBindingState", combo);
+ }
+
+ ReportLegacyCrashUma(info, has_valid_dump);
+ NotifyObservers(info.process_host_id, reported_counts);
+}
+
+void CrashMetricsReporter::NotifyObservers(
+ int rph_id,
+ const CrashMetricsReporter::ReportedCrashTypeSet& reported_counts) {
+ async_observers_->Notify(
+ FROM_HERE, &CrashMetricsReporter::Observer::OnCrashDumpProcessed, rph_id,
+ reported_counts);
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/browser/crash_metrics_reporter_android.h b/chromium/components/crash/content/browser/crash_metrics_reporter_android.h
new file mode 100644
index 00000000000..3dcf044cabb
--- /dev/null
+++ b/chromium/components/crash/content/browser/crash_metrics_reporter_android.h
@@ -0,0 +1,91 @@
+// Copyright 2018 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 COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_METRICS_REPORTER_ANDROID_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_METRICS_REPORTER_ANDROID_H_
+
+#include "base/containers/flat_set.h"
+#include "base/observer_list_threadsafe.h"
+#include "components/crash/content/browser/child_exit_observer_android.h"
+#include "components/crash/content/browser/crash_dump_manager_android.h"
+
+namespace crash_reporter {
+
+// Reports crash metrics about child processes to UMA, which is used as ground
+// truth for child process stability. This class should be used by any code that
+// wants to observe reason for the death of a child process.
+class CrashMetricsReporter {
+ public:
+ // This enum is used to back a UMA histogram, and must be treated as
+ // append-only.
+ enum ExitStatus {
+ EMPTY_MINIDUMP_WHILE_RUNNING,
+ EMPTY_MINIDUMP_WHILE_PAUSED,
+ EMPTY_MINIDUMP_WHILE_BACKGROUND,
+ VALID_MINIDUMP_WHILE_RUNNING,
+ VALID_MINIDUMP_WHILE_PAUSED,
+ VALID_MINIDUMP_WHILE_BACKGROUND,
+ MINIDUMP_STATUS_COUNT
+ };
+
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
+ enum class ProcessedCrashCounts {
+ kGpuForegroundOom = 0,
+ kRendererForegroundVisibleOom = 1,
+ kRendererForegroundIntentionalKill = 2,
+ kRendererForegroundVisibleSubframeOom = 3,
+ kRendererForegroundVisibleSubframeIntentionalKill = 4,
+ kRendererForegroundVisibleCrash = 5,
+ kRendererForegroundVisibleSubframeCrash = 6,
+ kGpuCrashAll = 7,
+ kRendererCrashAll = 8,
+ kRendererForegroundVisibleMainFrameIntentionalKill = 9,
+ kRendererForegroundVisibleNormalTermNoMinidump = 10,
+ kRendererForegroundInvisibleWithStrongBindingKilled = 11,
+ kRendererForegroundInvisibleWithStrongBindingOom = 12,
+ kRendererForegroundInvisibleWithModerateBindingKilled = 13,
+ kRendererForegroundInvisibleWithModerateBindingOom = 14,
+ kMaxValue = kRendererForegroundInvisibleWithModerateBindingOom
+ };
+ using ReportedCrashTypeSet = base::flat_set<ProcessedCrashCounts>;
+
+ // Careful note: the CrashMetricsReporter observers are asynchronous, and are
+ // notified via PostTask. This could be problematic with a large number of
+ // observers. Consider using a middle-layer observer to fan out synchronously
+ // to leaf observers if you need many objects listening to these messages.
+ class Observer {
+ public:
+ // Called when child process is dead and minidump was processed.
+ // |reported_counts| is a set of recorded metrics about child process
+ // crashes. It could be empty if no metrics were recorded.
+ virtual void OnCrashDumpProcessed(
+ int rph_id,
+ const ReportedCrashTypeSet& reported_counts) = 0;
+ };
+
+ static CrashMetricsReporter* GetInstance();
+
+ // Can be called on any thread.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ void CrashDumpProcessed(const ChildExitObserver::TerminationInfo& info,
+ breakpad::CrashDumpManager::CrashDumpStatus status);
+
+ private:
+ CrashMetricsReporter();
+ ~CrashMetricsReporter();
+
+ void NotifyObservers(int rph_id, const ReportedCrashTypeSet& reported_counts);
+
+ scoped_refptr<base::ObserverListThreadSafe<CrashMetricsReporter::Observer>>
+ async_observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashMetricsReporter);
+};
+
+} // namespace crash_reporter
+
+#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_METRICS_REPORTER_ANDROID_H_
diff --git a/chromium/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc b/chromium/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
new file mode 100644
index 00000000000..4367690dcdc
--- /dev/null
+++ b/chromium/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright 2018 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 "components/crash/content/browser/crash_metrics_reporter_android.h"
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crash_reporter {
+
+class CrashMetricsReporterObserver : public CrashMetricsReporter::Observer {
+ public:
+ CrashMetricsReporterObserver() {}
+ ~CrashMetricsReporterObserver() {}
+
+ // CrashMetricsReporter::Observer:
+ void OnCrashDumpProcessed(int rph_id,
+ const CrashMetricsReporter::ReportedCrashTypeSet&
+ reported_counts) override {
+ recorded_crash_types_ = reported_counts;
+ wait_run_loop_.QuitClosure().Run();
+ }
+
+ const CrashMetricsReporter::ReportedCrashTypeSet& recorded_crash_types()
+ const {
+ return recorded_crash_types_;
+ }
+
+ void WaitForProcessed() { wait_run_loop_.Run(); }
+
+ private:
+ base::RunLoop wait_run_loop_;
+ CrashMetricsReporter::ReportedCrashTypeSet recorded_crash_types_;
+ DISALLOW_COPY_AND_ASSIGN(CrashMetricsReporterObserver);
+};
+
+class CrashMetricsReporterTest : public testing::Test {
+ public:
+ CrashMetricsReporterTest()
+ : scoped_environment_(
+ base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
+ ~CrashMetricsReporterTest() override {}
+
+ static void CreateAndProcessCrashDump(
+ const ChildExitObserver::TerminationInfo& info,
+ const std::string& data) {
+ base::ScopedFD fd =
+ breakpad::CrashDumpManager::GetInstance()->CreateMinidumpFileForChild(
+ info.process_host_id);
+ EXPECT_TRUE(fd.is_valid());
+ base::WriteFileDescriptor(fd.get(), data.data(), data.size());
+ base::ScopedTempDir dump_dir;
+ EXPECT_TRUE(dump_dir.CreateUniqueTempDir());
+ breakpad::CrashDumpManager::GetInstance()->ProcessMinidumpFileFromChild(
+ dump_dir.GetPath(), info);
+ }
+
+ protected:
+ void TestOomCrashProcessing(
+ const ChildExitObserver::TerminationInfo& termination_info,
+ CrashMetricsReporter::ReportedCrashTypeSet expected_crash_types,
+ const char* histogram_name) {
+ base::HistogramTester histogram_tester;
+
+ CrashMetricsReporterObserver crash_dump_observer;
+ CrashMetricsReporter::GetInstance()->AddObserver(&crash_dump_observer);
+
+ CrashMetricsReporter::GetInstance()->CrashDumpProcessed(
+ termination_info,
+ breakpad::CrashDumpManager::CrashDumpStatus::kEmptyDump);
+ crash_dump_observer.WaitForProcessed();
+
+ EXPECT_EQ(expected_crash_types, crash_dump_observer.recorded_crash_types());
+
+ if (histogram_name) {
+ histogram_tester.ExpectUniqueSample(
+ histogram_name, CrashMetricsReporter::EMPTY_MINIDUMP_WHILE_RUNNING,
+ 1);
+ }
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_environment_;
+ DISALLOW_COPY_AND_ASSIGN(CrashMetricsReporterTest);
+};
+
+TEST_F(CrashMetricsReporterTest, RendereMainFrameOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ TestOomCrashProcessing(termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleOom},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, GpuProcessOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_GPU;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ TestOomCrashProcessing(
+ termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::kGpuForegroundOom},
+ "GPU.GPUProcessDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, RendererSubframeOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ termination_info.renderer_was_subframe = true;
+ TestOomCrashProcessing(termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleSubframeOom},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, RendererNonVisibleStrongOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_oom_protected_status = true;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.renderer_has_visible_clients = false;
+ TestOomCrashProcessing(termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithStrongBindingOom},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, RendererNonVisibleModerateOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::MODERATE;
+ termination_info.was_oom_protected_status = true;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.renderer_has_visible_clients = false;
+ TestOomCrashProcessing(
+ termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithModerateBindingOom},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, IntentionalKillIsNotOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = true;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ TestOomCrashProcessing(
+ termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundIntentionalKill,
+ CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleMainFrameIntentionalKill},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, NormalTerminationIsNotOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = true;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ TestOomCrashProcessing(termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleNormalTermNoMinidump},
+ nullptr);
+}
+
+TEST_F(CrashMetricsReporterTest, RendererForegroundCrash) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = true;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = true;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+
+ CrashMetricsReporterObserver crash_dump_observer;
+ CrashMetricsReporter::GetInstance()->AddObserver(&crash_dump_observer);
+
+ CrashMetricsReporter::GetInstance()->CrashDumpProcessed(
+ termination_info,
+ breakpad::CrashDumpManager::CrashDumpStatus::kValidDump);
+ crash_dump_observer.WaitForProcessed();
+
+ EXPECT_EQ(
+ CrashMetricsReporter::ReportedCrashTypeSet(
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundIntentionalKill,
+ CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleCrash,
+ CrashMetricsReporter::ProcessedCrashCounts::kRendererCrashAll}),
+ crash_dump_observer.recorded_crash_types());
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/tools/generate_breakpad_symbols.py b/chromium/components/crash/content/tools/generate_breakpad_symbols.py
index 24250f81fa1..2032f1991ad 100755
--- a/chromium/components/crash/content/tools/generate_breakpad_symbols.py
+++ b/chromium/components/crash/content/tools/generate_breakpad_symbols.py
@@ -29,24 +29,6 @@ BINARY_INFO = collections.namedtuple('BINARY_INFO',
['platform', 'arch', 'hash', 'name'])
-def GetCommandOutput(command, env=None):
- """Runs the command list, returning its output (stdout).
-
- Args:
- command: (list of strings) a command with arguments
-
- env: (dict or None) optional environment for the command. If None,
- inherits the existing environment, otherwise completely overrides it.
-
- From chromium_utils.
- """
- devnull = open(os.devnull, 'w')
- proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull,
- bufsize=1, env=env)
- output = proc.communicate()[0]
- return output
-
-
def GetDumpSymsBinary(build_dir=None):
"""Returns the path to the dump_syms binary."""
DUMP_SYMS = 'dump_syms'
@@ -70,8 +52,7 @@ def Resolve(path, exe_path, loader_path, rpaths):
path = path.replace('@executable_path', exe_path)
if path.find('@rpath') != -1:
for rpath in rpaths:
- new_path = Resolve(path.replace('@rpath', rpath), exe_path, loader_path,
- [])
+ new_path = path.replace('@rpath', rpath)
if os.access(new_path, os.X_OK):
return new_path
return ''
@@ -82,7 +63,10 @@ def GetSharedLibraryDependenciesLinux(binary):
"""Return absolute paths to all shared library dependecies of the binary.
This implementation assumes that we're running on a Linux system."""
- ldd = GetCommandOutput(['ldd', binary])
+ # TODO(thakis): Figure out how to make this work for android
+ # (https://crbug.com/849904) and use check_output().
+ p = subprocess.Popen(['ldd', binary], stdout=subprocess.PIPE)
+ ldd = p.communicate()[0]
lib_re = re.compile('\t.* => (.+) \(.*\)$')
result = []
for line in ldd.splitlines():
@@ -106,7 +90,7 @@ def GetDeveloperDirMac():
if 'DEVELOPER_DIR' in os.environ:
candidate_paths.append(os.environ['DEVELOPER_DIR'])
candidate_paths.extend([
- GetCommandOutput(['xcode-select', '-p']).strip(),
+ subprocess.check_output(['xcode-select', '-p']).strip(),
# Most Mac 10.1[0-2] bots have at least one Xcode installed.
'/Applications/Xcode.app',
'/Applications/Xcode9.0.app',
@@ -125,27 +109,57 @@ def GetSharedLibraryDependenciesMac(binary, exe_path):
"""Return absolute paths to all shared library dependecies of the binary.
This implementation assumes that we're running on a Mac system."""
- loader_path = os.path.dirname(binary)
+ # realpath() serves two purposes:
+ # 1. If an executable is linked against a framework, it links against
+ # Framework.framework/Framework, which is a symlink to
+ # Framework.framework/Framework/Versions/A/Framework. rpaths are relative
+ # to the real location, so resolving the symlink is important.
+ # 2. It converts binary to an absolute path. If binary is just
+ # "foo.dylib" in the current directory, dirname() would return an empty
+ # string, causing "@loader_path/foo" to incorrectly expand to "/foo".
+ loader_path = os.path.dirname(os.path.realpath(binary))
env = os.environ.copy()
developer_dir = GetDeveloperDirMac()
if developer_dir:
env['DEVELOPER_DIR'] = developer_dir
- otool = GetCommandOutput(['otool', '-l', binary], env=env).splitlines()
+ otool = subprocess.check_output(['otool', '-l', binary], env=env).splitlines()
rpaths = []
+ dylib_id = None
for idx, line in enumerate(otool):
if line.find('cmd LC_RPATH') != -1:
m = re.match(' *path (.*) \(offset .*\)$', otool[idx+2])
- rpaths.append(m.group(1))
-
- otool = GetCommandOutput(['otool', '-L', binary], env=env).splitlines()
+ rpath = m.group(1)
+ rpath = rpath.replace('@loader_path', loader_path)
+ rpath = rpath.replace('@executable_path', exe_path)
+ rpaths.append(rpath)
+ elif line.find('cmd LC_ID_DYLIB') != -1:
+ m = re.match(' *name (.*) \(offset .*\)$', otool[idx+2])
+ dylib_id = m.group(1)
+ # `man dyld` says that @rpath is resolved against a stack of LC_RPATHs from
+ # all executable images leading to the load of the current module. This is
+ # intentionally not implemented here, since we require that every .dylib
+ # contains all the rpaths it needs on its own, without relying on rpaths of
+ # the loading executables.
+
+ otool = subprocess.check_output(['otool', '-L', binary], env=env).splitlines()
lib_re = re.compile('\t(.*) \(compatibility .*\)$')
deps = []
for line in otool:
m = lib_re.match(line)
if m:
+ # For frameworks and shared libraries, `otool -L` prints the LC_ID_DYLIB
+ # as the first line. Filter that out.
+ if m.group(1) == dylib_id:
+ continue
dep = Resolve(m.group(1), exe_path, loader_path, rpaths)
if dep:
deps.append(os.path.normpath(dep))
+ else:
+ print >>sys.stderr, (
+ 'ERROR: failed to resolve %s, exe_path %s, loader_path %s, '
+ 'rpaths %s' % (m.group(1), exe_path, loader_path,
+ ', '.join(rpaths)))
+ sys.exit(1)
return deps
@@ -211,7 +225,7 @@ def GenerateSymbols(options, binaries):
break
binary_info = GetBinaryInfoFromHeaderInfo(
- GetCommandOutput([dump_syms, '-i', binary]).splitlines()[0])
+ subprocess.check_output([dump_syms, '-i', binary]).splitlines()[0])
if not binary_info:
should_dump_syms = False
reason = "Could not obtain binary information."