mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-08 12:19:05 +02:00
Crash annotations in content processes are currently sent over IPC via shared memory buffers. To pave the way for the Rust rewrite of the exception handler we are removing this code and gathering all the crash annotations within the content processes themselves. This patch causes annotations to be stored in the global table of each content process. They are then streamed out to the parent process by the exception handler together with the exception-time annotations. This has a number of benefits: * we have one less channel to exchange data between content processes and the parent process * we save memory because we don't need to allocate the shared memory buffers * annotations are faster because we don't stream them all out every time one changes * we won't truncate annotations anymore if we run out of space in the shared segment. * we don't need delayed annotations anymore, so we can get rid of the associated machinery As I refactored the code I tried to adjust all the obsolete comments, consolidate shared code and remove the redundant steps that were sometimes present. In many places we had two entire crash annotation tables we merged to change just a couple; that comes from the fact that historically we loaded them from disk. Now it doesn't matter anymore and we can just go ahead and change the ones we care about. Differential Revision: https://phabricator.services.mozilla.com/D62586 --HG-- extra : moz-landing-system : lando
242 lines
8.9 KiB
C++
242 lines
8.9 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "CrashReporterHost.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/Sprintf.h"
|
|
#include "mozilla/SyncRunnable.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "nsICrashService.h"
|
|
#include "nsXULAppAPI.h"
|
|
|
|
// Consistency checking for nsICrashService constants. We depend on the
|
|
// equivalence between nsICrashService values and GeckoProcessType values
|
|
// in the code below. Making them equal also ensures that if new process
|
|
// types are added, people will know they may need to add crash reporting
|
|
// support in various places because compilation errors will be triggered here.
|
|
static_assert(nsICrashService::PROCESS_TYPE_MAIN ==
|
|
(int)GeckoProcessType_Default,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_PLUGIN ==
|
|
(int)GeckoProcessType_Plugin,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_CONTENT ==
|
|
(int)GeckoProcessType_Content,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_IPDLUNITTEST ==
|
|
(int)GeckoProcessType_IPDLUnitTest,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_GMPLUGIN ==
|
|
(int)GeckoProcessType_GMPlugin,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_GPU == (int)GeckoProcessType_GPU,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_VR == (int)GeckoProcessType_VR,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_RDD == (int)GeckoProcessType_RDD,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_SOCKET ==
|
|
(int)GeckoProcessType_Socket,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_SANDBOX_BROKER ==
|
|
(int)GeckoProcessType_RemoteSandboxBroker,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
static_assert(nsICrashService::PROCESS_TYPE_FORKSERVER ==
|
|
(int)GeckoProcessType_ForkServer,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
// Add new static asserts here if you add more process types.
|
|
// Update this static assert as well.
|
|
static_assert(nsICrashService::PROCESS_TYPE_FORKSERVER + 1 ==
|
|
(int)GeckoProcessType_End,
|
|
"GeckoProcessType enum is out of sync with nsICrashService!");
|
|
|
|
namespace mozilla {
|
|
namespace ipc {
|
|
|
|
CrashReporterHost::CrashReporterHost(GeckoProcessType aProcessType,
|
|
CrashReporter::ThreadId aThreadId)
|
|
: mProcessType(aProcessType),
|
|
mThreadId(aThreadId),
|
|
mStartTime(::time(nullptr)),
|
|
mFinalized(false) {}
|
|
|
|
bool CrashReporterHost::GenerateCrashReport(base::ProcessId aPid) {
|
|
if (!TakeCrashedChildMinidump(aPid, nullptr)) {
|
|
return false;
|
|
}
|
|
return FinalizeCrashReport();
|
|
}
|
|
|
|
RefPtr<nsIFile> CrashReporterHost::TakeCrashedChildMinidump(
|
|
base::ProcessId aPid, uint32_t* aOutSequence) {
|
|
CrashReporter::AnnotationTable annotations;
|
|
MOZ_ASSERT(!HasMinidump());
|
|
|
|
RefPtr<nsIFile> crashDump;
|
|
if (!CrashReporter::TakeMinidumpForChild(aPid, getter_AddRefs(crashDump),
|
|
annotations, aOutSequence)) {
|
|
return nullptr;
|
|
}
|
|
if (!AdoptMinidump(crashDump, annotations)) {
|
|
return nullptr;
|
|
}
|
|
return crashDump;
|
|
}
|
|
|
|
bool CrashReporterHost::AdoptMinidump(nsIFile* aFile,
|
|
const AnnotationTable& aAnnotations) {
|
|
if (!CrashReporter::GetIDFromMinidump(aFile, mDumpID)) {
|
|
return false;
|
|
}
|
|
|
|
MergeCrashAnnotations(mExtraAnnotations, aAnnotations);
|
|
return true;
|
|
}
|
|
|
|
int32_t CrashReporterHost::GetCrashType() {
|
|
if (mExtraAnnotations[CrashReporter::Annotation::PluginHang].EqualsLiteral(
|
|
"1")) {
|
|
return nsICrashService::CRASH_TYPE_HANG;
|
|
}
|
|
|
|
return nsICrashService::CRASH_TYPE_CRASH;
|
|
}
|
|
|
|
bool CrashReporterHost::FinalizeCrashReport() {
|
|
MOZ_ASSERT(!mFinalized);
|
|
MOZ_ASSERT(HasMinidump());
|
|
|
|
mExtraAnnotations[CrashReporter::Annotation::ProcessType] =
|
|
XRE_ChildProcessTypeToAnnotation(mProcessType);
|
|
|
|
char startTime[32];
|
|
SprintfLiteral(startTime, "%lld", static_cast<long long>(mStartTime));
|
|
mExtraAnnotations[CrashReporter::Annotation::StartupTime] =
|
|
nsDependentCString(startTime);
|
|
|
|
CrashReporter::WriteExtraFile(mDumpID, mExtraAnnotations);
|
|
|
|
RecordCrash(mProcessType, GetCrashType(), mDumpID);
|
|
|
|
mFinalized = true;
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
void CrashReporterHost::RecordCrash(GeckoProcessType aProcessType,
|
|
int32_t aCrashType,
|
|
const nsString& aChildDumpID) {
|
|
if (!NS_IsMainThread()) {
|
|
RefPtr<Runnable> runnable = NS_NewRunnableFunction(
|
|
"ipc::CrashReporterHost::RecordCrash", [&]() -> void {
|
|
CrashReporterHost::RecordCrash(aProcessType, aCrashType,
|
|
aChildDumpID);
|
|
});
|
|
RefPtr<nsIThread> mainThread = do_GetMainThread();
|
|
SyncRunnable::DispatchToThread(mainThread, runnable);
|
|
return;
|
|
}
|
|
|
|
RecordCrashWithTelemetry(aProcessType, aCrashType);
|
|
NotifyCrashService(aProcessType, aCrashType, aChildDumpID);
|
|
}
|
|
|
|
/* static */
|
|
void CrashReporterHost::RecordCrashWithTelemetry(GeckoProcessType aProcessType,
|
|
int32_t aCrashType) {
|
|
nsCString key;
|
|
|
|
if (aProcessType == GeckoProcessType_Plugin &&
|
|
aCrashType == nsICrashService::CRASH_TYPE_HANG) {
|
|
key.AssignLiteral("pluginhang");
|
|
} else {
|
|
switch (aProcessType) {
|
|
#define GECKO_PROCESS_TYPE(enum_name, string_name, xre_name, bin_type) \
|
|
case GeckoProcessType_##enum_name: \
|
|
key.AssignLiteral(string_name); \
|
|
break;
|
|
#include "mozilla/GeckoProcessTypes.h"
|
|
#undef GECKO_PROCESS_TYPE
|
|
// We can't really hit this, thanks to the above switch, but having it
|
|
// here will placate the compiler.
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("unknown process type");
|
|
}
|
|
}
|
|
|
|
Telemetry::Accumulate(Telemetry::SUBPROCESS_CRASHES_WITH_DUMP, key, 1);
|
|
}
|
|
|
|
/* static */
|
|
void CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
|
|
int32_t aCrashType,
|
|
const nsString& aChildDumpID) {
|
|
MOZ_ASSERT(!aChildDumpID.IsEmpty());
|
|
|
|
nsCOMPtr<nsICrashService> crashService =
|
|
do_GetService("@mozilla.org/crashservice;1");
|
|
if (!crashService) {
|
|
return;
|
|
}
|
|
|
|
int32_t processType;
|
|
|
|
switch (aProcessType) {
|
|
case GeckoProcessType_IPDLUnitTest:
|
|
case GeckoProcessType_Default:
|
|
NS_ERROR("unknown process type");
|
|
return;
|
|
default:
|
|
processType = (int)aProcessType;
|
|
break;
|
|
}
|
|
|
|
RefPtr<Promise> promise;
|
|
crashService->AddCrash(processType, aCrashType, aChildDumpID,
|
|
getter_AddRefs(promise));
|
|
}
|
|
|
|
void CrashReporterHost::AddAnnotation(CrashReporter::Annotation aKey,
|
|
bool aValue) {
|
|
mExtraAnnotations[aKey] =
|
|
aValue ? NS_LITERAL_CSTRING("1") : NS_LITERAL_CSTRING("0");
|
|
}
|
|
|
|
void CrashReporterHost::AddAnnotation(CrashReporter::Annotation aKey,
|
|
int aValue) {
|
|
nsAutoCString valueString;
|
|
valueString.AppendInt(aValue);
|
|
mExtraAnnotations[aKey] = valueString;
|
|
}
|
|
|
|
void CrashReporterHost::AddAnnotation(CrashReporter::Annotation aKey,
|
|
unsigned int aValue) {
|
|
nsAutoCString valueString;
|
|
valueString.AppendInt(aValue);
|
|
mExtraAnnotations[aKey] = valueString;
|
|
}
|
|
|
|
void CrashReporterHost::AddAnnotation(CrashReporter::Annotation aKey,
|
|
const nsACString& aValue) {
|
|
mExtraAnnotations[aKey] = aValue;
|
|
}
|
|
|
|
bool CrashReporterHost::IsLikelyOOM() {
|
|
// The data is only populated during the call to `FinalizeCrashReport()`.
|
|
MOZ_ASSERT(mFinalized);
|
|
|
|
// If `OOMAllocationSize` was set, we know that the crash happened
|
|
// because an allocation failed (`malloc` returned `nullptr`).
|
|
//
|
|
// As Unix systems generally allow `malloc` to return a non-null value
|
|
// even when no virtual memory is available, this doesn't cover all
|
|
// cases of OOM under Unix (far from it).
|
|
return mExtraAnnotations[CrashReporter::Annotation::OOMAllocationSize]
|
|
.Length() > 0;
|
|
}
|
|
|
|
} // namespace ipc
|
|
} // namespace mozilla
|