--- a/ipc/glue/CrashReporterClient.cpp
+++ b/ipc/glue/CrashReporterClient.cpp
@@ -21,17 +21,18 @@ CrashReporterClient::CrashReporterClient
}
CrashReporterClient::~CrashReporterClient()
{
MOZ_COUNT_DTOR(CrashReporterClient);
}
void
-CrashReporterClient::AnnotateCrashReport(const nsCString& aKey, const nsCString& aData)
+CrashReporterClient::AnnotateCrashReport(CrashReporter::Annotation aKey,
+ const nsCString& aData)
{
StaticMutexAutoLock lock(sLock);
mMetadata->AnnotateCrashReport(aKey, aData);
}
void
CrashReporterClient::AppendAppNotes(const nsCString& aData)
{
--- a/ipc/glue/CrashReporterClient.h
+++ b/ipc/glue/CrashReporterClient.h
@@ -55,17 +55,18 @@ public:
aOutShmem);
}
static void InitSingletonWithShmem(const Shmem& aShmem);
static void DestroySingleton();
static RefPtr<CrashReporterClient> GetSingleton();
- void AnnotateCrashReport(const nsCString& aKey, const nsCString& aData);
+ void AnnotateCrashReport(CrashReporter::Annotation aKey,
+ const nsCString& aData);
void AppendAppNotes(const nsCString& aData);
private:
explicit CrashReporterClient(const Shmem& aShmem);
~CrashReporterClient();
private:
static StaticMutex sLock;
--- a/ipc/glue/CrashReporterHost.cpp
+++ b/ipc/glue/CrashReporterHost.cpp
@@ -59,17 +59,17 @@ CrashReporterHost::AdoptMinidump(nsIFile
}
bool
CrashReporterHost::FinalizeCrashReport()
{
MOZ_ASSERT(!mFinalized);
MOZ_ASSERT(HasMinidump());
- CrashReporter::AnnotationTable notes;
+ CrashReporter::AnnotationTable annotations;
nsAutoCString type;
switch (mProcessType) {
case GeckoProcessType_Content:
type = NS_LITERAL_CSTRING("content");
break;
case GeckoProcessType_Plugin:
case GeckoProcessType_GMPlugin:
@@ -77,32 +77,33 @@ CrashReporterHost::FinalizeCrashReport()
break;
case GeckoProcessType_GPU:
type = NS_LITERAL_CSTRING("gpu");
break;
default:
NS_ERROR("unknown process type");
break;
}
- notes.Put(NS_LITERAL_CSTRING("ProcessType"), type);
+ annotations[CrashReporter::Annotation::ProcessType] = type;
char startTime[32];
SprintfLiteral(startTime, "%lld", static_cast<long long>(mStartTime));
- notes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime));
+ annotations[CrashReporter::Annotation::StartupTime] =
+ nsDependentCString(startTime);
// We might not have shmem (for example, when running crashreporter tests).
if (mShmem.IsReadable()) {
- CrashReporterMetadataShmem::ReadAppNotes(mShmem, ¬es);
+ CrashReporterMetadataShmem::ReadAppNotes(mShmem, annotations);
}
- CrashReporter::AppendExtraData(mDumpID, mExtraNotes);
- CrashReporter::AppendExtraData(mDumpID, notes);
+ CrashReporter::AppendExtraData(mDumpID, mExtraAnnotations);
+ CrashReporter::AppendExtraData(mDumpID, annotations);
- // Use mExtraNotes, since NotifyCrashService looks for "PluginHang" which is
- // set in the parent process.
- NotifyCrashService(mProcessType, mDumpID, &mExtraNotes);
+ // Use mExtraAnnotations, since NotifyCrashService looks for "PluginHang"
+ // which is set in the parent process.
+ NotifyCrashService(mProcessType, mDumpID, mExtraAnnotations);
mFinalized = true;
return true;
}
namespace {
class GenerateMinidumpShutdownBlocker : public nsIAsyncShutdownBlocker {
public:
@@ -215,21 +216,21 @@ CrashReporterHost::GenerateMinidumpAndPa
getter_AddRefs(mTargetDump),
Move(callback),
aAsync);
}
/* static */ void
CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
const nsString& aChildDumpID,
- const AnnotationTable* aNotes)
+ const AnnotationTable& aNotes)
{
if (!NS_IsMainThread()) {
RefPtr<Runnable> runnable = NS_NewRunnableFunction(
- "ipc::CrashReporterHost::NotifyCrashService", [=]() -> void {
+ "ipc::CrashReporterHost::NotifyCrashService", [&]() -> void {
CrashReporterHost::NotifyCrashService(
aProcessType, aChildDumpID, aNotes);
});
RefPtr<nsIThread> mainThread = do_GetMainThread();
SyncRunnable::DispatchToThread(mainThread, runnable);
return;
}
@@ -249,19 +250,18 @@ CrashReporterHost::NotifyCrashService(Ge
switch (aProcessType) {
case GeckoProcessType_Content:
processType = nsICrashService::PROCESS_TYPE_CONTENT;
telemetryKey.AssignLiteral("content");
break;
case GeckoProcessType_Plugin: {
processType = nsICrashService::PROCESS_TYPE_PLUGIN;
telemetryKey.AssignLiteral("plugin");
- nsAutoCString val;
- if (aNotes->Get(NS_LITERAL_CSTRING("PluginHang"), &val) &&
- val.EqualsLiteral("1")) {
+ nsCString val = aNotes[CrashReporter::Annotation::PluginHang];
+ if (val.Equals(NS_LITERAL_CSTRING("1"))) {
crashType = nsICrashService::CRASH_TYPE_HANG;
telemetryKey.AssignLiteral("pluginhang");
}
break;
}
case GeckoProcessType_GMPlugin:
processType = nsICrashService::PROCESS_TYPE_GMPLUGIN;
telemetryKey.AssignLiteral("gmplugin");
@@ -276,15 +276,41 @@ CrashReporterHost::NotifyCrashService(Ge
}
nsCOMPtr<nsISupports> promise;
crashService->AddCrash(processType, crashType, aChildDumpID, getter_AddRefs(promise));
Telemetry::Accumulate(Telemetry::SUBPROCESS_CRASHES_WITH_DUMP, telemetryKey, 1);
}
void
-CrashReporterHost::AddNote(const nsCString& aKey, const nsCString& aValue)
+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)
{
- mExtraNotes.Put(aKey, 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 nsCString& aValue)
+{
+ mExtraAnnotations[aKey] = aValue;
}
} // namespace ipc
} // namespace mozilla
--- a/ipc/glue/CrashReporterHost.h
+++ b/ipc/glue/CrashReporterHost.h
@@ -125,19 +125,22 @@ public:
// This is a static helper function to notify the crash service that a
// crash has occurred. When PCrashReporter is removed, we can make this
// a member function. This can be called from any thread, and if not
// called from the main thread, will post a synchronous message to the
// main thread.
static void NotifyCrashService(
GeckoProcessType aProcessType,
const nsString& aChildDumpID,
- const AnnotationTable* aNotes);
+ const AnnotationTable& aNotes);
- void AddNote(const nsCString& aKey, const nsCString& aValue);
+ void AddAnnotation(CrashReporter::Annotation aKey, bool aValue);
+ void AddAnnotation(CrashReporter::Annotation aKey, int aValue);
+ void AddAnnotation(CrashReporter::Annotation aKey, unsigned int aValue);
+ void AddAnnotation(CrashReporter::Annotation aKey, const nsCString& aValue);
bool HasMinidump() const {
return !mDumpID.IsEmpty();
}
const nsString& MinidumpID() const {
MOZ_ASSERT(HasMinidump());
return mDumpID;
}
@@ -147,17 +150,17 @@ private:
const nsString& aChildDumpID);
private:
CallbackWrapper<bool> mCreateMinidumpCallback;
GeckoProcessType mProcessType;
Shmem mShmem;
ThreadId mThreadId;
time_t mStartTime;
- AnnotationTable mExtraNotes;
+ AnnotationTable mExtraAnnotations;
nsString mDumpID;
bool mFinalized;
nsCOMPtr<nsIFile> mTargetDump;
};
} // namespace ipc
} // namespace mozilla
--- a/ipc/glue/CrashReporterMetadataShmem.cpp
+++ b/ipc/glue/CrashReporterMetadataShmem.cpp
@@ -1,21 +1,24 @@
/* -*- 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 "CrashReporterMetadataShmem.h"
#include "mozilla/Attributes.h"
+#include "mozilla/EnumeratedRange.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace ipc {
+using CrashReporter::Annotation;
+
enum class EntryType : uint8_t {
None,
Annotation,
};
CrashReporterMetadataShmem::CrashReporterMetadataShmem(const Shmem& aShmem)
: mShmem(aShmem)
{
@@ -23,41 +26,42 @@ CrashReporterMetadataShmem::CrashReporte
}
CrashReporterMetadataShmem::~CrashReporterMetadataShmem()
{
MOZ_COUNT_DTOR(CrashReporterMetadataShmem);
}
void
-CrashReporterMetadataShmem::AnnotateCrashReport(const nsCString& aKey, const nsCString& aData)
+CrashReporterMetadataShmem::AnnotateCrashReport(Annotation aKey,
+ const nsCString& aData)
{
- mNotes.Put(aKey, aData);
+ mAnnotations[aKey] = aData;
SyncNotesToShmem();
}
void
CrashReporterMetadataShmem::AppendAppNotes(const nsCString& aData)
{
mAppNotes.Append(aData);
- mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes);
+ mAnnotations[Annotation::Notes] = mAppNotes;
SyncNotesToShmem();
}
class MOZ_STACK_CLASS MetadataShmemWriter
{
public:
explicit MetadataShmemWriter(const Shmem& aShmem)
: mCursor(aShmem.get<uint8_t>()),
mEnd(mCursor + aShmem.Size<uint8_t>())
{
*mCursor = uint8_t(EntryType::None);
}
- MOZ_MUST_USE bool WriteAnnotation(const nsCString& aKey, const nsCString& aValue) {
+ MOZ_MUST_USE bool WriteAnnotation(Annotation aKey, const nsCString& aValue) {
// This shouldn't happen because Commit() guarantees mCursor < mEnd. But
// we might as well be safe.
if (mCursor >= mEnd) {
return false;
}
// Save the current position so we can write the entry type if the entire
// entry fits.
@@ -120,21 +124,21 @@ private:
uint8_t* mEnd;
};
void
CrashReporterMetadataShmem::SyncNotesToShmem()
{
MetadataShmemWriter writer(mShmem);
- for (auto it = mNotes.Iter(); !it.Done(); it.Next()) {
- nsCString key = nsCString(it.Key());
- nsCString value = nsCString(it.Data());
- if (!writer.WriteAnnotation(key, value)) {
- return;
+ for (auto key : MakeEnumeratedRange(Annotation::Count)) {
+ if (!mAnnotations[key].IsEmpty()) {
+ if (!writer.WriteAnnotation(key, mAnnotations[key])) {
+ return;
+ }
}
}
}
// Helper class to iterate over metadata entries encoded in shmem.
class MOZ_STACK_CLASS MetadataShmemReader
{
public:
@@ -157,36 +161,37 @@ public:
void Next() {
if (mCursor < mEnd) {
mEntryType = EntryType(*mCursor++);
} else {
mEntryType = EntryType::None;
}
}
+ template <typename T>
+ bool Read(T* aOut) {
+ return Read(aOut, sizeof(T));
+ }
+
bool Read(nsCString& aOut) {
uint32_t length = 0;
if (!Read(&length)) {
return false;
}
const uint8_t* src = Read(length);
if (!src) {
return false;
}
aOut.Assign((const char *)src, length);
return true;
}
private:
- template <typename T>
- bool Read(T* aOut) {
- return Read(aOut, sizeof(T));
- }
bool Read(void* aOut, size_t aLength) {
const uint8_t* src = Read(aLength);
if (!src) {
return false;
}
memcpy(aOut, src, aLength);
return true;
}
@@ -204,27 +209,29 @@ private:
private:
const uint8_t* mCursor;
const uint8_t* mEnd;
EntryType mEntryType;
};
void
-CrashReporterMetadataShmem::ReadAppNotes(const Shmem& aShmem, CrashReporter::AnnotationTable* aNotes)
+CrashReporterMetadataShmem::ReadAppNotes(const Shmem& aShmem,
+ AnnotationTable& aNotes)
{
for (MetadataShmemReader reader(aShmem); !reader.Done(); reader.Next()) {
switch (reader.Type()) {
case EntryType::Annotation: {
- nsCString key, value;
- if (!reader.Read(key) || !reader.Read(value)) {
+ Annotation key;
+ nsCString value;
+ if (!reader.Read(&key) || !reader.Read(value)) {
return;
}
- aNotes->Put(key, value);
+ aNotes[key] = value;
break;
}
default:
NS_ASSERTION(false, "Unknown metadata entry type");
break;
}
}
}
--- a/ipc/glue/CrashReporterMetadataShmem.h
+++ b/ipc/glue/CrashReporterMetadataShmem.h
@@ -20,27 +20,29 @@ class CrashReporterMetadataShmem
typedef mozilla::ipc::Shmem Shmem;
typedef CrashReporter::AnnotationTable AnnotationTable;
public:
explicit CrashReporterMetadataShmem(const Shmem& aShmem);
~CrashReporterMetadataShmem();
// Metadata writers. These must only be called in child processes.
- void AnnotateCrashReport(const nsCString& aKey, const nsCString& aData);
+ void AnnotateCrashReport(CrashReporter::Annotation aKey,
+ const nsCString& aData);
void AppendAppNotes(const nsCString& aData);
- static void ReadAppNotes(const Shmem& aShmem, CrashReporter::AnnotationTable* aNotes);
+ static void ReadAppNotes(const Shmem& aShmem,
+ CrashReporter::AnnotationTable& aNotes);
private:
void SyncNotesToShmem();
private:
Shmem mShmem;
- AnnotationTable mNotes;
+ AnnotationTable mAnnotations;
nsCString mAppNotes;
};
} // namespace ipc
} // namespace mozilla
#endif // mozilla_ipc_CrashReporterMetadataShmem_h
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -2713,20 +2713,19 @@ Gecko_AddBufferToCrashReport(const void*
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsICrashReporter> cr = do_GetService("@mozilla.org/toolkit/crash-reporter;1");
NS_ENSURE_TRUE_VOID(cr);
cr->RegisterAppMemory((uint64_t) addr, len);
}
void
-Gecko_AnnotateCrashReport(const char* key_str, const char* value_str)
+Gecko_AnnotateCrashReport(uint32_t key, const char* value_str)
{
MOZ_ASSERT(NS_IsMainThread());
- nsDependentCString key(key_str);
nsDependentCString value(value_str);
nsCOMPtr<nsICrashReporter> cr = do_GetService("@mozilla.org/toolkit/crash-reporter;1");
NS_ENSURE_TRUE_VOID(cr);
cr->AnnotateCrashReport(key, value);
}
void
Gecko_ContentList_AppendAll(
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -277,28 +277,16 @@ static const DllBlockInfo sWindowsDllBlo
#ifndef STATUS_DLL_NOT_FOUND
#define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
#endif
// define this for very verbose dll load debug spew
#undef DEBUG_very_verbose
-static const char kBlockedDllsParameter[] = "BlockedDllList=";
-static const int kBlockedDllsParameterLen =
- sizeof(kBlockedDllsParameter) - 1;
-
-static const char kBlocklistInitFailedParameter[] = "BlocklistInitFailed=1\n";
-static const int kBlocklistInitFailedParameterLen =
- sizeof(kBlocklistInitFailedParameter) - 1;
-
-static const char kUser32BeforeBlocklistParameter[] = "User32BeforeBlocklist=1\n";
-static const int kUser32BeforeBlocklistParameterLen =
- sizeof(kUser32BeforeBlocklistParameter) - 1;
-
static uint32_t sInitFlags;
static bool sBlocklistInitAttempted;
static bool sBlocklistInitFailed;
static bool sUser32BeforeBlocklist;
// Duplicated from xpcom glue. Ideally this should be shared.
void
printf_stderr(const char *fmt, ...)
@@ -953,32 +941,38 @@ DllBlocklist_Initialize(uint32_t aInitFl
if (pProc) {
gStartAddressesToBlock->append(pProc);
}
}
#endif
}
MFBT_API void
-DllBlocklist_WriteNotes(HANDLE file)
+DllBlocklist_WriteNotes(HANDLE file, const char* aBlockedDllListAnnotation,
+ const char* aBlocklistInitFailedAnnotation,
+ const char* aUser32BeforeBlocklistAnnotation)
{
DWORD nBytes;
- WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr);
+ WriteFile(file, aBlockedDllListAnnotation, strlen(aBlockedDllListAnnotation),
+ &nBytes, nullptr);
+ WriteFile(file, "=", 1, &nBytes, nullptr);
DllBlockSet::Write(file);
WriteFile(file, "\n", 1, &nBytes, nullptr);
if (sBlocklistInitFailed) {
- WriteFile(file, kBlocklistInitFailedParameter,
- kBlocklistInitFailedParameterLen, &nBytes, nullptr);
+ WriteFile(file, aBlocklistInitFailedAnnotation,
+ strlen(aBlocklistInitFailedAnnotation), &nBytes, nullptr);
+ WriteFile(file, "=1", 2, &nBytes, nullptr);
}
if (sUser32BeforeBlocklist) {
- WriteFile(file, kUser32BeforeBlocklistParameter,
- kUser32BeforeBlocklistParameterLen, &nBytes, nullptr);
+ WriteFile(file, aUser32BeforeBlocklistAnnotation,
+ strlen(aUser32BeforeBlocklistAnnotation), &nBytes, nullptr);
+ WriteFile(file, "=1", 2, &nBytes, nullptr);
}
}
MFBT_API bool
DllBlocklist_CheckStatus()
{
if (sBlocklistInitFailed || sUser32BeforeBlocklist)
return false;
--- a/mozglue/build/WindowsDllBlocklist.h
+++ b/mozglue/build/WindowsDllBlocklist.h
@@ -16,17 +16,20 @@
enum DllBlocklistInitFlags
{
eDllBlocklistInitFlagDefault = 0,
eDllBlocklistInitFlagIsChildProcess = 1
};
MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags = eDllBlocklistInitFlagDefault);
-MFBT_API void DllBlocklist_WriteNotes(HANDLE file);
+MFBT_API void DllBlocklist_WriteNotes(HANDLE file,
+ const char* aBlockedDllListAnnotation,
+ const char* aBlocklistInitFailedAnnotation,
+ const char* aUser32BeforeBlocklistAnnotation);
MFBT_API bool DllBlocklist_CheckStatus();
// Forward declaration
namespace mozilla {
namespace glue {
namespace detail {
class DllServicesBase;
} // namespace detail
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -1942,17 +1942,17 @@ extern "C" {
extern "C" {
pub fn Gecko_SetJemallocThreadLocalArena(enabled: bool);
}
extern "C" {
pub fn Gecko_AddBufferToCrashReport(addr: *const ::std::os::raw::c_void, len: usize);
}
extern "C" {
pub fn Gecko_AnnotateCrashReport(
- key_str: *const ::std::os::raw::c_char,
+ key: u32,
value_str: *const ::std::os::raw::c_char,
);
}
extern "C" {
pub fn Servo_Element_ClearData(node: RawGeckoElementBorrowed);
}
extern "C" {
pub fn Servo_Element_SizeOfExcludingThisAndCVs(
--- a/toolkit/components/crashes/CrashManager.jsm
+++ b/toolkit/components/crashes/CrashManager.jsm
@@ -216,53 +216,16 @@ this.CrashManager.prototype = Object.fre
// The following are return codes for individual event file processing.
// File processed OK.
EVENT_FILE_SUCCESS: "ok",
// The event appears to be malformed.
EVENT_FILE_ERROR_MALFORMED: "malformed",
// The type of event is unknown.
EVENT_FILE_ERROR_UNKNOWN_EVENT: "unknown-event",
- // A whitelist of crash annotations which do not contain sensitive data
- // and are saved in the crash record and sent with Firefox Health Report.
- ANNOTATION_WHITELIST: [
- "AsyncShutdownTimeout",
- "BuildID",
- "ipc_channel_error",
- "ProductID",
- "ProductName",
- "ReleaseChannel",
- "RemoteType",
- "SecondsSinceLastCrash",
- "ShutdownProgress",
- "StartupCrash",
- "TelemetryEnvironment",
- "Version",
- // The following entries are not normal annotations that can be found in
- // the .extra file but are included in the crash record/FHR:
- "AvailablePageFile",
- "AvailablePhysicalMemory",
- "AvailableVirtualMemory",
- "BlockedDllList",
- "BlocklistInitFailed",
- "ContainsMemoryReport",
- "CrashTime",
- "EventLoopNestingLevel",
- "IsGarbageCollecting",
- "MozCrashReason",
- "OOMAllocationSize",
- "SystemMemoryUsePercentage",
- "TextureUsage",
- "TotalPageFile",
- "TotalPhysicalMemory",
- "TotalVirtualMemory",
- "UptimeTS",
- "User32BeforeBlocklist",
- ],
-
/**
* Obtain a list of all dumps pending upload.
*
* The returned value is a promise that resolves to an array of objects
* on success. Each element in the array has the following properties:
*
* id (string)
* The ID of the crash (a UUID).
@@ -631,20 +594,26 @@ this.CrashManager.prototype = Object.fre
let payload = data.substring(start);
return this._handleEventFilePayload(store, entry, type, date, payload);
})();
},
_filterAnnotations(annotations) {
let filteredAnnotations = {};
+ let crashReporter = Cc["@mozilla.org/toolkit/crash-reporter;1"]
+ .getService(Ci.nsICrashReporter);
for (let line in annotations) {
- if (this.ANNOTATION_WHITELIST.includes(line)) {
- filteredAnnotations[line] = annotations[line];
+ try {
+ if (crashReporter.isAnnotationWhitelistedForPing(line)) {
+ filteredAnnotations[line] = annotations[line];
+ }
+ } catch (e) {
+ // Silently drop unknown annotations
}
}
return filteredAnnotations;
},
_sendCrashPing(crashId, type, date, metadata = {}) {
// If we have a saved environment, use it. Otherwise report
--- a/toolkit/components/crashes/tests/xpcshell/crash.extra
+++ b/toolkit/components/crashes/tests/xpcshell/crash.extra
@@ -3,17 +3,16 @@ TelemetryEnvironment={"EscapedField":"Es
EMCheckCompatibility=true
ProductName=Firefox
ContentSandboxCapabilities=119
TelemetryClientId=
Vendor=Mozilla
InstallTime=1000000000
Theme=classic/1.0
ReleaseChannel=default
-AddonsShouldHaveBlockedE10s=1
ServerURL=https://crash-reports.mozilla.com
SafeMode=0
ContentSandboxCapable=1
useragent_locale=en-US
Version=55.0a1
BuildID=20170512114708
ProductID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
TelemetryServerURL=
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/CrashAnnotations.cpp
@@ -0,0 +1,63 @@
+/* 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 "CrashAnnotations.h"
+
+#include <algorithm>
+#include <cstring>
+#include <iterator>
+
+using std::begin;
+using std::end;
+using std::find_if;
+
+namespace CrashReporter {
+
+bool
+AnnotationFromString(Annotation& aResult, const char* aValue)
+{
+ auto elem = find_if(
+ begin(kAnnotationStrings),
+ end(kAnnotationStrings),
+ [&aValue](const char* aString) {
+ return strcmp(aString, aValue) == 0;
+ }
+ );
+
+ if (elem == end(kAnnotationStrings)) {
+ return false;
+ }
+
+ aResult = static_cast<Annotation>(elem - begin(kAnnotationStrings));
+ return true;
+}
+
+bool
+IsAnnotationWhitelistedForPing(Annotation aAnnotation) {
+ auto elem = find_if(
+ begin(kCrashPingWhitelist),
+ end(kCrashPingWhitelist),
+ [&aAnnotation](Annotation aElement) {
+ return aElement == aAnnotation;
+ }
+ );
+
+ return elem != end(kCrashPingWhitelist);
+}
+
+bool
+IsAnnotationBlacklistedForContent(Annotation aAnnotation)
+{
+ auto elem = find_if(
+ begin(kContentProcessBlacklist),
+ end(kContentProcessBlacklist),
+ [&aAnnotation](Annotation aElement) {
+ return aElement == aAnnotation;
+ }
+ );
+
+ return elem != end(kContentProcessBlacklist);
+}
+
+} // namespace CrashReporter
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/CrashAnnotations.h.in
@@ -0,0 +1,76 @@
+/* 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/. */
+
+#ifndef CrashAnnotations_h
+#define CrashAnnotations_h
+
+#include <cstdint>
+
+namespace CrashReporter {
+
+// Typed enum representing all crash annotations
+enum class Annotation : uint32_t {
+${enum}
+};
+
+// Stringified crash annotation names
+const char* const kAnnotationStrings[] = {
+${strings}
+};
+
+// Whitelist of crash annotations that can be included in a crash ping
+const Annotation kCrashPingWhitelist[] = {
+${whitelist}
+};
+
+// Blacklist of crash annotations that shouldn't be read from a content process
+const Annotation kContentProcessBlacklist[] = {
+${blacklist}
+};
+
+/**
+ * Return the string representation of a crash annotation.
+ *
+ * @param aAnnotation a crash annotation
+ * @returns A constant string holding the annotation name
+ */
+static inline const char*
+AnnotationToString(Annotation aAnnotation) {
+ return kAnnotationStrings[static_cast<uint32_t>(aAnnotation)];
+}
+
+/**
+ * Converts a string to its corresponding crash annotation.
+ *
+ * @param aResult a reference where the annotation will be stored
+ * @param aValue the string to be converted
+ * @return true if the string was successfully converted, false if it did not
+ * correspond to any known annotation
+ */
+bool AnnotationFromString(Annotation& aResult, const char* aValue);
+
+/**
+ * Checks if the given crash annotation is whitelisted for inclusion in the
+ * crash ping.
+ *
+ * @param aAnnotation the crash annotation to be checked
+ * @return true if the annotation can be included in the crash ping, false
+ * otherwise
+ */
+bool IsAnnotationWhitelistedForPing(Annotation aAnnotation);
+
+/**
+ * Checks if the given crash annotation needs to be filtered out when reading
+ * a content process crash annotations. Blacklisted annotations will be
+ * replaced with ones provided by the parent process.
+ *
+ * @param aAnnotation the crash annotation to be checked
+ * @return true if the annotation needs to be filtered out when reading
+ * annotations provided by a content process, false otherwise
+ */
+bool IsAnnotationBlacklistedForContent(Annotation aAnnotation);
+
+} // namespace CrashReporter
+
+#endif // CrashAnnotations_h
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/CrashAnnotations.yaml
@@ -0,0 +1,209 @@
+# This lists all the available crash annotations.
+#
+# Mandatory fields for each entry are:
+# - description: A string describing the annotation
+# - type: the annotation type, currently `string`, `integer` or `boolean`.
+# The latter are stringified to `1` for true and `0` for false.
+#
+# Additionally a field can have the following optional fields:
+# - altname: A string that will be used when writing out the annotation to the
+# .extra file instead of the annotation name
+# - ping: A boolean that indicates whether the annotation is whitelisted for
+# going into the crash ping, if not specified this defaults to false
+# - content: A boolean that indicates whether the field will be included in
+# subprocess reports, if not specified this defaults to true
+
+AvailablePageFile:
+ description: >
+ Windows-only, maximum amount of memory that can be committed. This
+ annotation is populated with the contents of the MEMORYSTATUSEX's structure
+ ullAvailPageFile field.
+ type: string
+ ping: true
+
+AvailablePhysicalMemory:
+ description: >
+ Windows-only, amount of free physical memory in bytes. This annotation
+ is populated with the contents of the MEMORYSTATUSEX's structure
+ ullAvailPhys field.
+ type: string
+ ping: true
+
+AvailableVirtualMemory:
+ description: >
+ Windows-only, amount of free virtual memory in bytes. This annotation is
+ populated with the contents of the MEMORYSTATUSEX's structure
+ ullAvailVirtual field.
+ type: string
+ ping: true
+
+BlockedDllList:
+ description: >
+ Comma-separated list of blocked DLLS, Windows-only
+ type: string
+ ping: true
+
+BlocklistInitFailed:
+ description: >
+ Set to 1 if the DLL blocklist could not be initialized.
+ type: boolean
+ ping: true
+
+BreakpadReserveAddress:
+ description: >
+ Address of the buffer reserved by Breakpad.
+ type: string
+
+BreakpadReserveSize:
+ description: >
+ Size of the buffer reserved by Breakpad.
+ type: string
+
+ContainsMemoryReport:
+ description: >
+ Indicates that the crash dump contains a memory report.
+ type: boolean
+ ping: true
+
+CrashTime:
+ description: >
+ Crash time in seconds since the Epoch.
+ type: string
+ ping: true
+
+EventLoopNestingLevel:
+ description: >
+ Present only if higher than 0, indicates that we're running in a nested
+ event loop and indicates the nesting level.
+ type: integer
+ ping: true
+
+InstallTime:
+ description: >
+ The time when Firefox was installed expressed as seconds since the Epoch
+ type: integer
+
+IsGarbageCollecting:
+ description: >
+ If true then the JavaScript garbage collector was running when the crash
+ occurred.
+ type: boolean
+ ping: true
+
+MozCrashReason:
+ description: >
+ Plaintext description of why Firefox crashed, this is usually set by
+ assertions and the like.
+ type: string
+ ping: true
+
+Notes:
+ description: >
+ Miscellaneous notes that can be appended to a crash.
+ type: string
+
+OOMAllocationSize:
+ description: >
+ Size of the allocation that caused an out-of-memory condition.
+ type: string
+ ping: true
+
+PluginHang:
+ description: >
+ The presence of this annotation indicates that this crash was generated in
+ response to a plugin hanging.
+ type: boolean
+
+ProcessType:
+ description: >
+ Type of the process that crashed, can hold the values "content", "plugin" or
+ "gpu" currently.
+ type: string
+
+SecondsSinceLastCrash:
+ description: >
+ Time in seconds since the last crash occurred.
+ type: string
+ ping: true
+
+ServerURL:
+ description: >
+ URL used to post the crash report.
+ type: string
+
+StartupTime:
+ description: >
+ The time when Firefox was launched expressed in seconds since the Epoch.
+ type: integer
+ content: false
+
+SystemMemoryUsePercentage:
+ description: >
+ Windows-only, percentage of physical memory in use. This annotation is
+ populated with the contents of the MEMORYSTATUSEX's structure dwMemoryLoad
+ field.
+ type: integer
+ ping: true
+
+TelemetrySessionId:
+ description: >
+ Telemetry session ID.
+ type: string
+
+TestKey:
+ description: >
+ Annotation used in tests.
+ type: string
+
+TestUnicode:
+ description: >
+ Annotation used in tests.
+ type: string
+
+TextureUsage:
+ description: >
+ Amount of memory in bytes consumed by textures.
+ type: string
+ ping: true
+
+ThreadIdNameMapping:
+ description: >
+ List of thread names with their corresponding thread IDs.
+ type: string
+
+TotalPageFile:
+ description: >
+ Windows-only, current committed memory limit. This annotation is
+ populated with the contents of the MEMORYSTATUSEX's structure
+ ullTotalPageFile field.
+ type: string
+ ping: true
+
+TotalPhysicalMemory:
+ description: >
+ Windows-only, amount of physical memory in bytes. This annotation is
+ populated with the contents of the MEMORYSTATUSEX's structure
+ ullTotalPhys field.
+ type: string
+ ping: true
+
+TotalVirtualMemory:
+ description: >
+ Windows-only, size of the virtual address space. This annotation is
+ populated with the contents of the MEMORYSTATUSEX's structure
+ ullTotalVirtual field.
+ type: string
+ ping: true
+
+UptimeTS:
+ description: >
+ Uptime in seconds. This annotation uses a string instead of an integer
+ because it has a fractional component.
+ type: string
+ ping: true
+
+User32BeforeBlocklist:
+ description: >
+ Set to 1 if user32.dll was loaded before we could install the DLL blocklist.
+ type: boolean
+ ping: true
--- a/toolkit/crashreporter/client/moz.build
+++ b/toolkit/crashreporter/client/moz.build
@@ -3,16 +3,17 @@
# 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/.
if CONFIG['OS_TARGET'] != 'Android':
Program('crashreporter')
UNIFIED_SOURCES += [
+ '../CrashAnnotations.cpp',
'crashreporter.cpp',
'ping.cpp',
]
LOCAL_INCLUDES += [
'/toolkit/components/jsoncpp/include',
]
--- a/toolkit/crashreporter/client/ping.cpp
+++ b/toolkit/crashreporter/client/ping.cpp
@@ -15,16 +15,18 @@
#elif defined(XP_MACOSX)
#include <CoreFoundation/CoreFoundation.h>
#elif defined(XP_WIN)
#include <objbase.h>
#endif
#include "json/json.h"
+#include "CrashAnnotations.h"
+
using std::string;
namespace CrashReporter {
struct UUID {
uint32_t m0;
uint16_t m1;
uint16_t m2;
@@ -115,54 +117,25 @@ const char kTelemetryUrl[] = "Tele
const char kTelemetrySessionId[] = "TelemetrySessionId";
const int kTelemetryVersion = 4;
// Create the payload.metadata node of the crash ping using fields extracted
// from the .extra file
static Json::Value
CreateMetadataNode(StringTable& strings)
{
- // The following list should be kept in sync with the one in CrashManager.jsm
- const char *entries[] = {
- "AsyncShutdownTimeout",
- "AvailablePageFile",
- "AvailablePhysicalMemory",
- "AvailableVirtualMemory",
- "BlockedDllList",
- "BlocklistInitFailed",
- "BuildID",
- "ContainsMemoryReport",
- "CrashTime",
- "EventLoopNestingLevel",
- "ipc_channel_error",
- "IsGarbageCollecting",
- "MozCrashReason",
- "OOMAllocationSize",
- "ProductID",
- "ProductName",
- "ReleaseChannel",
- "RemoteType",
- "SecondsSinceLastCrash",
- "ShutdownProgress",
- "StartupCrash",
- "SystemMemoryUsePercentage",
- "TextureUsage",
- "TotalPageFile",
- "TotalPhysicalMemory",
- "TotalVirtualMemory",
- "UptimeTS",
- "User32BeforeBlocklist",
- "Version",
- };
-
Json::Value node;
- for (auto entry : entries) {
- if ((strings.find(entry) != strings.end()) && !strings[entry].empty()) {
- node[entry] = strings[entry];
+ for (auto line : strings) {
+ Annotation annotation;
+
+ if (AnnotationFromString(annotation, line.first.c_str())) {
+ if (IsAnnotationWhitelistedForPing(annotation)) {
+ node[line.first] = line.second;
+ }
}
}
return node;
}
// Create the payload node of the crash ping
static Json::Value
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/generate_crash_reporter_sources.py
@@ -0,0 +1,176 @@
+# 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/.
+
+import string
+import sys
+import yaml
+
+###############################################################################
+# Language-agnostic functionality #
+###############################################################################
+
+
+def read_annotations(annotations_filename):
+ """Read the annotations from a YAML file.
+ If an error is encountered quit the program."""
+
+ try:
+ with open(annotations_filename, 'r') as annotations_file:
+ annotations = yaml.safe_load(annotations_file)
+
+ except (IOError, ValueError) as e:
+ print("Error parsing " + annotations_filename + ":\n" + str(e) + "\n")
+ sys.exit(1)
+
+ return annotations
+
+
+def read_template(template_filename):
+ """Read the contents of the template.
+ If an error is encountered quit the program."""
+
+ try:
+ with open(template_filename, "r") as template_file:
+ template = template_file.read()
+
+ except IOError as ex:
+ print("Error when reading " + template_filename + ":\n" + str(ex) + "\n")
+ sys.exit(1)
+
+ return template
+
+
+def extract_crash_ping_whitelist(annotations):
+ """Extract an array holding the names of the annotations whitelisted for
+ inclusion in the crash ping."""
+
+ return [name
+ for (name, data)
+ in annotations.iteritems()
+ if data.get('ping', False)]
+
+
+def extract_content_process_blacklist(annotations):
+ """Extract an array holding the names of the annotations blacklisted when
+ read from a content process."""
+
+ return [name
+ for (name, data)
+ in annotations.iteritems()
+ if not data.get('content', True)]
+
+###############################################################################
+# C++ code generation #
+###############################################################################
+
+
+def generate_strings(annotations):
+ """Generate strings corresponding to every annotation."""
+
+ names = [" \"" + data.get('altname', name) + "\""
+ for (name, data)
+ in annotations.iteritems()]
+
+ return ',\n'.join(names)
+
+
+def generate_enum(annotations):
+ """Generate the C++ typed enum holding all the annotations and return it
+ as a string."""
+
+ enum = ""
+
+ for i, (name, _) in enumerate(annotations.iteritems()):
+ enum += " " + name + " = " + str(i) + ",\n"
+
+ enum += " Count = " + str(len(annotations))
+
+ return enum
+
+
+def generate_array_initializer(contents):
+ """Generates the initializer for a C++ array of annotations."""
+
+ initializer = [" Annotation::" + name for name in contents]
+
+ return ',\n'.join(initializer)
+
+
+def generate_header(template, annotations):
+ """Generate a header by filling the template with the the list of
+ annotations and return it as a string."""
+
+ whitelist = extract_crash_ping_whitelist(annotations)
+ blacklist = extract_content_process_blacklist(annotations)
+
+ return "/* This file was autogenerated by " + \
+ "toolkit/crashreporter/generate_crash_reporter_sources.py. " + \
+ "DO NOT EDIT */\n\n" + \
+ string.Template(template).substitute({
+ "enum": generate_enum(annotations),
+ "strings": generate_strings(annotations),
+ "whitelist": generate_array_initializer(whitelist),
+ "blacklist": generate_array_initializer(blacklist),
+ })
+
+
+def emit_header(output, template_filename, annotations_filename):
+ """Generate the C++ header from the template and write it out."""
+
+ annotations = read_annotations(annotations_filename)
+ template = read_template(template_filename)
+ generated_header = generate_header(template, annotations)
+
+ try:
+ output.write(generated_header)
+
+ except IOError as ex:
+ print("Error while writing out the generated file:\n" + str(ex) + "\n")
+ sys.exit(1)
+
+###############################################################################
+# IDL code generation #
+###############################################################################
+
+
+def generate_annotations_constants(annotations):
+ """Generate the list of IDL constants corresponding to the annotations
+ and return it as a string."""
+
+ constants = []
+
+ for i, (name, _) in enumerate(annotations.iteritems()):
+ constants.append(" const long " + name + " = " + str(i + 1) + ";")
+
+ return "\n".join(constants)
+
+
+def generate_idl(template, annotations):
+ """Generate the IDL interface by filling the template with the the list of
+ annotations and return it as a string."""
+
+ annotations_list = generate_annotations_constants(annotations)
+
+ return "/* This file was autogenerated by " + \
+ "toolkit/crashreporter/generate_crash_reporter_sources.py. " + \
+ "DO NOT EDIT */\n\n" + \
+ string.Template(template).substitute({
+ "annotations": annotations_list,
+ })
+
+
+def emit_idl(output, template_filename, annotations_filename):
+ """Generate the nsICrashReporter IDL interface from the template and write
+ it out."""
+
+ annotations = read_annotations(annotations_filename)
+ template = read_template(template_filename)
+ generated_idl = generate_idl(template, annotations)
+
+ try:
+ output.write(generated_idl)
+
+ except IOError as ex:
+ print("Error while writing out the generated file:\n" + str(ex) + "\n")
+ sys.exit(1)
--- a/toolkit/crashreporter/moz.build
+++ b/toolkit/crashreporter/moz.build
@@ -4,23 +4,29 @@
# 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/.
SPHINX_TREES['crashreporter'] = 'docs'
with Files('docs/**'):
SCHEDULES.exclusive = ['docs']
+GENERATED_FILES += [
+ 'CrashAnnotations.h',
+]
+
EXPORTS += [
+ '!CrashAnnotations.h',
'nsExceptionHandler.h',
]
JAR_MANIFESTS += ['jar.mn']
UNIFIED_SOURCES = [
+ 'CrashAnnotations.cpp',
'nsExceptionHandlerUtils.cpp',
]
FINAL_LIBRARY = 'xul'
if CONFIG['MOZ_CRASHREPORTER']:
if CONFIG['OS_ARCH'] == 'WINNT':
DIRS += [
@@ -118,11 +124,18 @@ if CONFIG['MOZ_CRASHREPORTER']:
if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
CXXFLAGS += ['-Wno-shadow']
else:
UNIFIED_SOURCES += [
'nsDummyExceptionHandler.cpp',
]
+# Generate CrashAnnotations.h
+crash_annotations = GENERATED_FILES['CrashAnnotations.h']
+crash_annotations.script = 'generate_crash_reporter_sources.py:emit_header'
+crash_annotations.inputs = [
+ 'CrashAnnotations.h.in',
+ 'CrashAnnotations.yaml',
+]
with Files('**'):
BUG_COMPONENT = ('Toolkit', 'Crash Reporting')
--- a/toolkit/crashreporter/nsDummyExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsDummyExceptionHandler.cpp
@@ -61,24 +61,41 @@ SetupExtraData(nsIFile* aAppDataDirector
nsresult
UnsetExceptionHandler()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
-AnnotateCrashReport(const nsACString& key,
- const nsACString& data)
+AnnotateCrashReport(Annotation key, bool data)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+AnnotateCrashReport(Annotation key, int data)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
-RemoveCrashReportAnnotation(const nsACString& key)
+AnnotateCrashReport(Annotation key, unsigned int data)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+AnnotateCrashReport(Annotation key, const nsACString& data)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+RemoveCrashReportAnnotation(Annotation key)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
SetGarbageCollecting(bool collecting)
{
return NS_ERROR_NOT_IMPLEMENTED;
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -7,16 +7,17 @@
#include "nsExceptionHandler.h"
#include "nsExceptionHandlerUtils.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryService.h"
#include "nsDataHashtable.h"
#include "mozilla/ArrayUtils.h"
+#include "mozilla/EnumeratedRange.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "mozilla/Unused.h"
#include "mozilla/Printf.h"
#include "mozilla/Sprintf.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/ipc/CrashReporterClient.h"
@@ -220,17 +221,17 @@ static XP_CHAR lastCrashTimeFilename[XP_
// with the current process that gets lost when we fork so we need to
// explicitly pass it to am
static char* androidUserSerial = nullptr;
#endif
// this holds additional data sent via the API
static Mutex* crashReporterAPILock;
static Mutex* notesFieldLock;
-static AnnotationTable* crashReporterAPIData_Hash;
+static AnnotationTable crashReporterAPIData_Table;
static nsCString* crashReporterAPIData = nullptr;
static nsCString* crashEventAPIData = nullptr;
static nsCString* notesField = nullptr;
static bool isGarbageCollecting;
static uint32_t eventloopNestingLevel = 0;
// Avoid a race during application termination.
static Mutex* dumpSafetyLock;
@@ -311,25 +312,16 @@ public:
NS_IMETHOD Run() override;
private:
uint32_t mPID;
};
#endif // MOZ_CRASHREPORTER_INJECTOR
-// Crashreporter annotations that we don't send along in subprocess reports.
-static const char* kSubprocessBlacklist[] = {
- "FramePoisonBase",
- "FramePoisonSize",
- "StartupCrash",
- "StartupTime",
- "URL"
-};
-
// If annotations are attempted before the crash reporter is enabled,
// they queue up here.
class DelayedNote;
nsTArray<nsAutoPtr<DelayedNote> >* gDelayedAnnotations;
#if defined(XP_WIN)
// the following are used to prevent other DLLs reverting the last chance
// exception handler to the windows default. Any attempt to change the
@@ -665,23 +657,26 @@ WriteString(PlatformWriter& pw, const ch
size_t len = my_strlen(str);
#else
size_t len = strlen(str);
#endif
pw.WriteBuffer(str, len);
}
-template<int N>
static void
-WriteAnnotation(PlatformWriter& pw, const char (&name)[N],
- const char* value) {
- WriteLiteral(pw, name);
+WriteAnnotation(PlatformWriter& pw, const Annotation name,
+ const char* value, size_t len = 0) {
+ WriteString(pw, AnnotationToString(name));
WriteLiteral(pw, "=");
- WriteString(pw, value);
+ if (len == 0) {
+ WriteString(pw, value);
+ } else {
+ pw.WriteBuffer(value, len);
+ }
WriteLiteral(pw, "\n");
};
/**
* If minidump_id is null, we assume that dump_path contains the full
* dump file path.
*/
static void
@@ -723,23 +718,28 @@ WriteGlobalMemoryStatus(PlatformWriter*
conversionFunc(statex.field, buffer, 10); \
if (apiData) { \
WriteAnnotation(*apiData, name, buffer); \
} \
if (eventFile) { \
WriteAnnotation(*eventFile, name, buffer); \
}
- WRITE_STATEX_FIELD(dwMemoryLoad, "SystemMemoryUsePercentage", ltoa);
- WRITE_STATEX_FIELD(ullTotalVirtual, "TotalVirtualMemory", _ui64toa);
- WRITE_STATEX_FIELD(ullAvailVirtual, "AvailableVirtualMemory", _ui64toa);
- WRITE_STATEX_FIELD(ullTotalPageFile, "TotalPageFile", _ui64toa);
- WRITE_STATEX_FIELD(ullAvailPageFile, "AvailablePageFile", _ui64toa);
- WRITE_STATEX_FIELD(ullTotalPhys, "TotalPhysicalMemory", _ui64toa);
- WRITE_STATEX_FIELD(ullAvailPhys, "AvailablePhysicalMemory", _ui64toa);
+ WRITE_STATEX_FIELD(dwMemoryLoad, Annotation::SystemMemoryUsePercentage,
+ ltoa);
+ WRITE_STATEX_FIELD(ullTotalVirtual, Annotation::TotalVirtualMemory,
+ _ui64toa);
+ WRITE_STATEX_FIELD(ullAvailVirtual, Annotation::AvailableVirtualMemory,
+ _ui64toa);
+ WRITE_STATEX_FIELD(ullTotalPageFile, Annotation::TotalPageFile, _ui64toa);
+ WRITE_STATEX_FIELD(ullAvailPageFile, Annotation::AvailablePageFile,
+ _ui64toa);
+ WRITE_STATEX_FIELD(ullTotalPhys, Annotation::TotalPhysicalMemory, _ui64toa);
+ WRITE_STATEX_FIELD(ullAvailPhys, Annotation::AvailablePhysicalMemory,
+ _ui64toa);
#undef WRITE_STATEX_FIELD
}
}
#endif
#if !defined(MOZ_WIDGET_ANDROID)
@@ -1019,101 +1019,108 @@ MinidumpCallback(
OpenAPIData(apiData, descriptor.path());
#else
OpenAPIData(apiData, dump_path, minidump_id);
#endif
apiData.WriteBuffer(crashReporterAPIData->get(), crashReporterAPIData->Length());
}
if (currentSessionId) {
- WriteAnnotation(apiData, "TelemetrySessionId", currentSessionId);
- WriteAnnotation(eventFile, "TelemetrySessionId", currentSessionId);
+ WriteAnnotation(apiData, Annotation::TelemetrySessionId,
+ currentSessionId);
+ WriteAnnotation(eventFile, Annotation::TelemetrySessionId,
+ currentSessionId);
}
- WriteAnnotation(apiData, "CrashTime", crashTimeString);
- WriteAnnotation(eventFile, "CrashTime", crashTimeString);
-
- WriteAnnotation(apiData, "UptimeTS", uptimeTSString);
- WriteAnnotation(eventFile, "UptimeTS", uptimeTSString);
+ WriteAnnotation(apiData, Annotation::CrashTime, crashTimeString);
+ WriteAnnotation(eventFile, Annotation::CrashTime, crashTimeString);
+
+ WriteAnnotation(apiData, Annotation::UptimeTS, uptimeTSString);
+ WriteAnnotation(eventFile, Annotation::UptimeTS, uptimeTSString);
if (timeSinceLastCrash != 0) {
- WriteAnnotation(apiData, "SecondsSinceLastCrash",
+ WriteAnnotation(apiData, Annotation::SecondsSinceLastCrash,
timeSinceLastCrashString);
- WriteAnnotation(eventFile, "SecondsSinceLastCrash",
+ WriteAnnotation(eventFile, Annotation::SecondsSinceLastCrash,
timeSinceLastCrashString);
}
if (isGarbageCollecting) {
- WriteAnnotation(apiData, "IsGarbageCollecting", "1");
- WriteAnnotation(eventFile, "IsGarbageCollecting", "1");
+ WriteAnnotation(apiData, Annotation::IsGarbageCollecting, "1");
+ WriteAnnotation(eventFile, Annotation::IsGarbageCollecting, "1");
}
char buffer[128];
if (eventloopNestingLevel > 0) {
XP_STOA(eventloopNestingLevel, buffer, 10);
- WriteAnnotation(apiData, "EventLoopNestingLevel", buffer);
- WriteAnnotation(eventFile, "EventLoopNestingLevel", buffer);
+ WriteAnnotation(apiData, Annotation::EventLoopNestingLevel, buffer);
+ WriteAnnotation(eventFile, Annotation::EventLoopNestingLevel, buffer);
}
#ifdef XP_WIN
if (gBreakpadReservedVM) {
_ui64toa(uintptr_t(gBreakpadReservedVM), buffer, 10);
- WriteAnnotation(apiData, "BreakpadReserveAddress", buffer);
+ WriteAnnotation(apiData, Annotation::BreakpadReserveAddress, buffer);
_ui64toa(kReserveSize, buffer, 10);
- WriteAnnotation(apiData, "BreakpadReserveSize", buffer);
+ WriteAnnotation(apiData, Annotation::BreakpadReserveSize, buffer);
}
#ifdef HAS_DLL_BLOCKLIST
if (apiData.Valid()) {
- DllBlocklist_WriteNotes(apiData.Handle());
- DllBlocklist_WriteNotes(eventFile.Handle());
+ DllBlocklist_WriteNotes(
+ apiData.Handle(),
+ AnnotationToString(Annotation::BlockedDllList),
+ AnnotationToString(Annotation::BlocklistInitFailed),
+ AnnotationToString(Annotation::User32BeforeBlocklist));
+
+ DllBlocklist_WriteNotes(
+ eventFile.Handle(),
+ AnnotationToString(Annotation::BlockedDllList),
+ AnnotationToString(Annotation::BlocklistInitFailed),
+ AnnotationToString(Annotation::User32BeforeBlocklist));
}
#endif
WriteGlobalMemoryStatus(&apiData, &eventFile);
#endif // XP_WIN
char* rust_panic_reason;
size_t rust_panic_len;
if (get_rust_panic_reason(&rust_panic_reason, &rust_panic_len)) {
// rust_panic_reason is not null-terminated.
- WriteLiteral(apiData, "MozCrashReason=");
- apiData.WriteBuffer(rust_panic_reason, rust_panic_len);
- WriteLiteral(apiData, "\n");
- WriteLiteral(eventFile, "MozCrashReason=");
- eventFile.WriteBuffer(rust_panic_reason, rust_panic_len);
- WriteLiteral(eventFile, "\n");
+ WriteAnnotation(apiData, Annotation::MozCrashReason, rust_panic_reason,
+ rust_panic_len);
+ WriteAnnotation(eventFile, Annotation::MozCrashReason, rust_panic_reason,
+ rust_panic_len);
} else if (gMozCrashReason) {
- WriteAnnotation(apiData, "MozCrashReason", gMozCrashReason);
- WriteAnnotation(eventFile, "MozCrashReason", gMozCrashReason);
+ WriteAnnotation(apiData, Annotation::MozCrashReason, gMozCrashReason);
+ WriteAnnotation(eventFile, Annotation::MozCrashReason, gMozCrashReason);
}
if (oomAllocationSizeBuffer[0]) {
- WriteAnnotation(apiData, "OOMAllocationSize", oomAllocationSizeBuffer);
- WriteAnnotation(eventFile, "OOMAllocationSize", oomAllocationSizeBuffer);
+ WriteAnnotation(apiData, Annotation::OOMAllocationSize,
+ oomAllocationSizeBuffer);
+ WriteAnnotation(eventFile, Annotation::OOMAllocationSize,
+ oomAllocationSizeBuffer);
}
if (texturesSizeBuffer[0]) {
- WriteAnnotation(apiData, "TextureUsage", texturesSizeBuffer);
- WriteAnnotation(eventFile, "TextureUsage", texturesSizeBuffer);
+ WriteAnnotation(apiData, Annotation::TextureUsage, texturesSizeBuffer);
+ WriteAnnotation(eventFile, Annotation::TextureUsage, texturesSizeBuffer);
}
if (memoryReportPath) {
- WriteLiteral(apiData, "ContainsMemoryReport=1\n");
- WriteLiteral(eventFile, "ContainsMemoryReport=1\n");
+ WriteAnnotation(apiData, Annotation::ContainsMemoryReport, "1");
+ WriteAnnotation(eventFile, Annotation::ContainsMemoryReport, "1");
}
std::function<void(const char*)> getThreadAnnotationCB =
- [&] (const char * aAnnotation) -> void {
- if (aAnnotation) {
- WriteLiteral(apiData, "ThreadIdNameMapping=");
- WriteLiteral(eventFile, "ThreadIdNameMapping=");
- WriteString(apiData, aAnnotation);
- WriteString(eventFile, aAnnotation);
- WriteLiteral(apiData, "\n");
- WriteLiteral(eventFile, "\n");
+ [&] (const char* aValue) -> void {
+ if (aValue) {
+ WriteAnnotation(apiData, Annotation::ThreadIdNameMapping, aValue);
+ WriteAnnotation(eventFile, Annotation::ThreadIdNameMapping, aValue);
}
};
GetFlatThreadAnnotation(getThreadAnnotationCB);
}
if (!doReport) {
#ifdef XP_WIN
TerminateProcess(GetCurrentProcess(), 1);
@@ -1270,36 +1277,34 @@ PrepareChildExceptionTimeAnnotations(voi
#endif
char oomAllocationSizeBuffer[32] = "";
if (gOOMAllocationSize) {
XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
}
if (oomAllocationSizeBuffer[0]) {
- WriteAnnotation(apiData, "OOMAllocationSize", oomAllocationSizeBuffer);
+ WriteAnnotation(apiData, Annotation::OOMAllocationSize,
+ oomAllocationSizeBuffer);
}
char* rust_panic_reason;
size_t rust_panic_len;
if (get_rust_panic_reason(&rust_panic_reason, &rust_panic_len)) {
// rust_panic_reason is not null-terminated.
- WriteLiteral(apiData, "MozCrashReason=");
- apiData.WriteBuffer(rust_panic_reason, rust_panic_len);
- WriteLiteral(apiData, "\n");
+ WriteAnnotation(apiData, Annotation::MozCrashReason, rust_panic_reason,
+ rust_panic_len);
} else if (gMozCrashReason) {
- WriteAnnotation(apiData, "MozCrashReason", gMozCrashReason);
+ WriteAnnotation(apiData, Annotation::MozCrashReason, gMozCrashReason);
}
std::function<void(const char*)> getThreadAnnotationCB =
- [&] (const char * aAnnotation) -> void {
- if (aAnnotation) {
- WriteLiteral(apiData, "ThreadIdNameMapping=");
- WriteString(apiData, aAnnotation);
- WriteLiteral(apiData, "\n");
+ [&] (const char * aValue) -> void {
+ if (aValue) {
+ WriteAnnotation(apiData, Annotation::ThreadIdNameMapping, aValue);
}
};
GetFlatThreadAnnotation(getThreadAnnotationCB);
}
#ifdef XP_WIN
static void
ReserveBreakpadVM()
@@ -1492,20 +1497,16 @@ nsresult SetExceptionHandler(nsIFile* aX
crashReporterAPIData = new nsCString();
crashEventAPIData = new nsCString();
NS_ASSERTION(!crashReporterAPILock, "Shouldn't have a lock yet");
crashReporterAPILock = new Mutex("crashReporterAPILock");
NS_ASSERTION(!notesFieldLock, "Shouldn't have a lock yet");
notesFieldLock = new Mutex("notesFieldLock");
- crashReporterAPIData_Hash =
- new nsDataHashtable<nsCStringHashKey,nsCString>();
- NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY);
-
notesField = new nsCString();
NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
#if !defined(MOZ_WIDGET_ANDROID)
// Locate the crash reporter executable
nsAutoString crashReporterPath_temp;
nsresult rv = LocateExecutable(aXREDirectory,
NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME),
@@ -1647,18 +1648,17 @@ nsresult SetExceptionHandler(nsIFile* aX
printf_stderr ("SetUnhandledExceptionFilter hook failed; crash reporter is vulnerable.\n");
#endif
#endif
// store application start time
char timeString[32];
time_t startupTime = time(nullptr);
XP_TTOA(startupTime, timeString, 10);
- AnnotateCrashReport(NS_LITERAL_CSTRING("StartupTime"),
- nsDependentCString(timeString));
+ AnnotateCrashReport(Annotation::StartupTime, nsDependentCString(timeString));
#if defined(XP_MACOSX)
// On OS X, many testers like to see the OS crash reporting dialog
// since it offers immediate stack traces. We allow them to set
// a default to pass exceptions to the OS handler.
Boolean keyExistsAndHasValidFormat = false;
Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("OSCrashReporter"),
kCFPreferencesCurrentApplication,
@@ -1900,17 +1900,17 @@ nsresult SetupExtraData(nsIFile* aAppDat
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString data;
if(NS_SUCCEEDED(GetOrInit(dataDirectory,
NS_LITERAL_CSTRING("InstallTime") + aBuildID,
data, InitInstallTime)))
- AnnotateCrashReport(NS_LITERAL_CSTRING("InstallTime"), data);
+ AnnotateCrashReport(Annotation::InstallTime, data);
// this is a little different, since we can't init it with anything,
// since it's stored at crash time, and we can't annotate the
// crash report with the stored value, since we really want
// (now - LastCrash), so we just get a value if it exists,
// and store it in a time_t value.
if(NS_SUCCEEDED(GetOrInit(dataDirectory, NS_LITERAL_CSTRING("LastCrash"),
data, nullptr))) {
@@ -1958,18 +1958,19 @@ nsresult UnsetExceptionHandler()
// allow SetUnhandledExceptionFilter
gBlockUnhandledExceptionFilter = false;
#endif
delete gExceptionHandler;
// do this here in the unlikely case that we succeeded in allocating
// our strings but failed to allocate gExceptionHandler.
- delete crashReporterAPIData_Hash;
- crashReporterAPIData_Hash = nullptr;
+ std::fill(crashReporterAPIData_Table.begin(),
+ crashReporterAPIData_Table.end(),
+ EmptyCString());
delete crashReporterAPILock;
crashReporterAPILock = nullptr;
delete notesFieldLock;
notesFieldLock = nullptr;
delete crashReporterAPIData;
@@ -2046,22 +2047,18 @@ static void ReplaceChar(nsCString& str,
str.BeginReading(iter);
iter.advance(pos + replacement.Length() - 1);
str.EndReading(end);
}
}
static nsresult
-EscapeAnnotation(const nsACString& key, const nsACString& data, nsCString& escapedData)
+EscapeAnnotation(const nsACString& data, nsCString& escapedData)
{
- if (FindInReadable(NS_LITERAL_CSTRING("="), key) ||
- FindInReadable(NS_LITERAL_CSTRING("\n"), key))
- return NS_ERROR_INVALID_ARG;
-
if (FindInReadable(NS_LITERAL_CSTRING("\0"), data))
return NS_ERROR_INVALID_ARG;
escapedData = data;
// escape backslashes
ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
NS_LITERAL_CSTRING("\\\\"));
@@ -2069,35 +2066,35 @@ EscapeAnnotation(const nsACString& key,
ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
NS_LITERAL_CSTRING("\\n"));
return NS_OK;
}
class DelayedNote
{
public:
- DelayedNote(const nsACString& aKey, const nsACString& aData)
- : mKey(aKey), mData(aData), mType(Annotation) {}
+ DelayedNote(Annotation aKey, const nsACString& aData)
+ : mKey(aKey), mData(aData), mType(CrashAnnotation) {}
explicit DelayedNote(const nsACString& aData)
: mData(aData), mType(AppNote) {}
void Run()
{
- if (mType == Annotation) {
+ if (mType == CrashAnnotation) {
AnnotateCrashReport(mKey, mData);
} else {
AppendAppNotesToCrashReport(mData);
}
}
private:
- nsCString mKey;
+ Annotation mKey;
nsCString mData;
- enum AnnotationType { Annotation, AppNote } mType;
+ enum AnnotationType { CrashAnnotation, AppNote } mType;
};
static void
EnqueueDelayedNote(DelayedNote* aNote)
{
if (!gDelayedAnnotations) {
gDelayedAnnotations = new nsTArray<nsAutoPtr<DelayedNote> >();
}
@@ -2111,64 +2108,86 @@ RunAndCleanUpDelayedNotes()
for (nsAutoPtr<DelayedNote>& note : *gDelayedAnnotations) {
note->Run();
}
delete gDelayedAnnotations;
gDelayedAnnotations = nullptr;
}
}
-nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
+nsresult AnnotateCrashReport(Annotation key, bool data)
+{
+ return AnnotateCrashReport(key, data ? NS_LITERAL_CSTRING("1")
+ : NS_LITERAL_CSTRING("0"));
+}
+
+nsresult AnnotateCrashReport(Annotation key, int data)
+{
+ nsAutoCString dataString;
+ dataString.AppendInt(data);
+
+ return AnnotateCrashReport(key, dataString);
+}
+
+nsresult AnnotateCrashReport(Annotation key, unsigned int data)
+{
+ nsAutoCString dataString;
+ dataString.AppendInt(data);
+
+ return AnnotateCrashReport(key, dataString);
+}
+
+nsresult AnnotateCrashReport(Annotation key, const nsACString& data)
{
if (!GetEnabled())
return NS_ERROR_NOT_INITIALIZED;
nsCString escapedData;
- nsresult rv = EscapeAnnotation(key, data, escapedData);
+ nsresult rv = EscapeAnnotation(data, escapedData);
if (NS_FAILED(rv))
return rv;
if (!XRE_IsParentProcess()) {
// The newer CrashReporterClient can be used from any thread.
if (RefPtr<CrashReporterClient> client = CrashReporterClient::GetSingleton()) {
- client->AnnotateCrashReport(nsCString(key), escapedData);
+ client->AnnotateCrashReport(key, escapedData);
return NS_OK;
}
// EnqueueDelayedNote() can only be called on the main thread.
MOZ_RELEASE_ASSERT(NS_IsMainThread());
EnqueueDelayedNote(new DelayedNote(key, data));
return NS_OK;
}
MutexAutoLock lock(*crashReporterAPILock);
- crashReporterAPIData_Hash->Put(key, escapedData);
+ crashReporterAPIData_Table[key] = escapedData;
// now rebuild the file contents
crashReporterAPIData->Truncate(0);
crashEventAPIData->Truncate(0);
- for (auto it = crashReporterAPIData_Hash->Iter(); !it.Done(); it.Next()) {
- const nsACString& key = it.Key();
- nsCString entry = it.Data();
+ for (auto key : MakeEnumeratedRange(Annotation::Count)) {
+ nsDependentCString str(AnnotationToString(key));
+ nsCString entry = crashReporterAPIData_Table[key];
if (!entry.IsEmpty()) {
NS_NAMED_LITERAL_CSTRING(kEquals, "=");
NS_NAMED_LITERAL_CSTRING(kNewline, "\n");
- nsAutoCString line = key + kEquals + entry + kNewline;
+ nsAutoCString line = str + kEquals + entry + kNewline;
crashReporterAPIData->Append(line);
crashEventAPIData->Append(line);
}
}
return NS_OK;
}
-nsresult RemoveCrashReportAnnotation(const nsACString& key)
+nsresult RemoveCrashReportAnnotation(Annotation key)
{
return AnnotateCrashReport(key, NS_LITERAL_CSTRING(""));
}
nsresult SetGarbageCollecting(bool collecting)
{
if (!GetEnabled())
return NS_ERROR_NOT_INITIALIZED;
@@ -2197,17 +2216,17 @@ nsresult AppendAppNotesToCrashReport(con
if (FindInReadable(NS_LITERAL_CSTRING("\0"), data))
return NS_ERROR_INVALID_ARG;
if (!XRE_IsParentProcess()) {
// Since we don't go through AnnotateCrashReport in the parent process,
// we must ensure that the data is escaped and valid before the parent
// sees it.
nsCString escapedData;
- nsresult rv = EscapeAnnotation(NS_LITERAL_CSTRING("Notes"), data, escapedData);
+ nsresult rv = EscapeAnnotation(data, escapedData);
if (NS_FAILED(rv))
return rv;
if (RefPtr<CrashReporterClient> client = CrashReporterClient::GetSingleton()) {
client->AppendAppNotes(escapedData);
return NS_OK;
}
@@ -2216,29 +2235,30 @@ nsresult AppendAppNotesToCrashReport(con
EnqueueDelayedNote(new DelayedNote(data));
return NS_OK;
}
MutexAutoLock lock(*notesFieldLock);
notesField->Append(data);
- return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField);
+ return AnnotateCrashReport(Annotation::Notes, *notesField);
}
// Returns true if found, false if not found.
static bool
-GetAnnotation(const nsACString& key, nsACString& data)
+GetAnnotation(CrashReporter::Annotation key, nsACString& data)
{
if (!gExceptionHandler)
return false;
- nsAutoCString entry;
- if (!crashReporterAPIData_Hash->Get(key, &entry))
+ nsCString entry = crashReporterAPIData_Table[key];
+ if (entry.IsEmpty()) {
return false;
+ }
data = entry;
return true;
}
nsresult RegisterAppMemory(void* ptr, size_t length)
{
if (!GetEnabled())
@@ -2276,25 +2296,24 @@ void SetIncludeContextHeap(bool aValue)
#endif
}
bool GetServerURL(nsACString& aServerURL)
{
if (!gExceptionHandler)
return false;
- return GetAnnotation(NS_LITERAL_CSTRING("ServerURL"), aServerURL);
+ return GetAnnotation(CrashReporter::Annotation::ServerURL, aServerURL);
}
nsresult SetServerURL(const nsACString& aServerURL)
{
// store server URL with the API data
// the client knows to handle this specially
- return AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
- aServerURL);
+ return AnnotateCrashReport(Annotation::ServerURL, aServerURL);
}
nsresult
SetRestartArgs(int argc, char** argv)
{
if (!gExceptionHandler)
return NS_OK;
@@ -2907,102 +2926,90 @@ AppendExtraData(const nsAString& id, con
if (!GetExtraFileForID(id, getter_AddRefs(extraFile)))
return false;
return AppendExtraData(extraFile, data);
}
//-----------------------------------------------------------------------------
// Helpers for AppendExtraData()
//
-struct Blacklist {
- Blacklist() : mItems(nullptr), mLen(0) { }
- Blacklist(const char** items, int len) : mItems(items), mLen(len) { }
-
- bool Contains(const nsACString& key) const {
- for (int i = 0; i < mLen; ++i)
- if (key.EqualsASCII(mItems[i]))
- return true;
- return false;
- }
-
- const char** mItems;
- const int mLen;
-};
-
static void
-WriteAnnotation(PRFileDesc* fd, const nsACString& key, const nsACString& value)
+WriteAnnotation(PRFileDesc* fd, const Annotation key, const nsACString& value)
{
- PR_Write(fd, key.BeginReading(), key.Length());
+ const char* annotation = AnnotationToString(key);
+ PR_Write(fd, annotation, strlen(annotation));
PR_Write(fd, "=", 1);
PR_Write(fd, value.BeginReading(), value.Length());
PR_Write(fd, "\n", 1);
}
template<int N>
static void
WriteLiteral(PRFileDesc* fd, const char (&str)[N])
{
PR_Write(fd, str, N - 1);
}
static bool
WriteExtraData(nsIFile* extraFile,
const AnnotationTable& data,
- const Blacklist& blacklist,
bool writeCrashTime=false,
- bool truncate=false)
+ bool truncate=false,
+ bool content=false)
{
PRFileDesc* fd;
int truncOrAppend = truncate ? PR_TRUNCATE : PR_APPEND;
nsresult rv =
extraFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | truncOrAppend,
0600, &fd);
if (NS_FAILED(rv))
return false;
- for (auto iter = data.ConstIter(); !iter.Done(); iter.Next()) {
- // Skip entries in the blacklist.
- const nsACString& key = iter.Key();
- if (blacklist.Contains(key)) {
- continue;
+ for (auto key : MakeEnumeratedRange(Annotation::Count)) {
+ nsCString value = data[key];
+ // Skip entries in the blacklist and empty entries.
+ if ((content && IsAnnotationBlacklistedForContent(key)) ||
+ value.IsEmpty()) {
+ continue;
}
- WriteAnnotation(fd, key, iter.Data());
+ WriteAnnotation(fd, key, value);
}
if (writeCrashTime) {
time_t crashTime = time(nullptr);
char crashTimeString[32];
XP_TTOA(crashTime, crashTimeString, 10);
WriteAnnotation(fd,
- nsDependentCString("CrashTime"),
+ Annotation::CrashTime,
nsDependentCString(crashTimeString));
double uptimeTS = (TimeStamp::NowLoRes() -
TimeStamp::ProcessCreation()).ToSecondsSigDigits();
char uptimeTSString[64];
SimpleNoCLibDtoA(uptimeTS, uptimeTSString, sizeof(uptimeTSString));
WriteAnnotation(fd,
- nsDependentCString("UptimeTS"),
+ Annotation::UptimeTS,
nsDependentCString(uptimeTSString));
}
if (memoryReportPath) {
- WriteLiteral(fd, "ContainsMemoryReport=1\n");
+ WriteAnnotation(fd, Annotation::ContainsMemoryReport,
+ NS_LITERAL_CSTRING("1"));
}
PR_Close(fd);
return true;
}
bool
AppendExtraData(nsIFile* extraFile, const AnnotationTable& data)
{
- return WriteExtraData(extraFile, data, Blacklist());
+ return WriteExtraData(extraFile, data);
}
static bool
IsDataEscaped(char* aData)
{
if (strchr(aData, '\n')) {
// There should not be any newlines
return false;
@@ -3037,45 +3044,53 @@ ReadAndValidateExceptionTimeAnnotations(
if (dataLen > 0 && data[dataLen - 1] == '\n') {
data[dataLen - 1] = 0;
--dataLen;
}
// There should not be any newlines in the key
if (strchr(line, '\n')) {
break;
}
+ // The annotation sould be known
+ Annotation annotation;
+ if (!AnnotationFromString(annotation, line)) {
+ break;
+ }
// Data should have been escaped by the child
if (!IsDataEscaped(data)) {
break;
}
// Looks good, save the (line,data) pair
- aAnnotations.Put(nsDependentCString(line),
- nsDependentCString(data, dataLen));
+ aAnnotations[annotation] = nsDependentCString(data, dataLen);
}
}
/**
+ * Writes extra data in the .extra file corresponding to the specified
+ * minidump. If `content` is set to true then this assumes that of a child
+ * process.
+ *
* NOTE: One side effect of this function is that it deletes the
* GeckoChildCrash<pid>.extra file if it exists, once processed.
*/
static bool
WriteExtraForMinidump(nsIFile* minidump,
uint32_t pid,
- const Blacklist& blacklist,
+ bool content,
nsIFile** extraFile)
{
nsCOMPtr<nsIFile> extra;
if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra))) {
return false;
}
- if (!WriteExtraData(extra, *crashReporterAPIData_Hash,
- blacklist,
+ if (!WriteExtraData(extra, crashReporterAPIData_Table,
true /*write crash time*/,
- true /*truncate*/)) {
+ true /*truncate*/,
+ content)) {
return false;
}
if (pid && processToCrashFd.count(pid)) {
PRFileDesc* prFd = processToCrashFd[pid];
processToCrashFd.erase(pid);
FILE* fd;
#if defined(XP_WIN)
@@ -3169,21 +3184,20 @@ OnChildProcessDumpRequested(void* aConte
uint32_t pid =
#ifdef XP_MACOSX
aClientInfo.pid();
#else
aClientInfo->pid();
#endif
- if (!WriteExtraForMinidump(minidump, pid,
- Blacklist(kSubprocessBlacklist,
- ArrayLength(kSubprocessBlacklist)),
- getter_AddRefs(extraFile)))
+ if (!WriteExtraForMinidump(minidump, pid, /* content */ true,
+ getter_AddRefs(extraFile))) {
return;
+ }
if (ShouldReport()) {
nsCOMPtr<nsIFile> memoryReport;
if (memoryReportPath) {
CreateFileFromPath(memoryReportPath, getter_AddRefs(memoryReport));
MOZ_ASSERT(memoryReport);
}
MoveToPending(minidump, extraFile, memoryReport);
@@ -3696,17 +3710,18 @@ PairedDumpCallbackExtra(
#ifdef XP_WIN32
nullptr, nullptr,
#endif
succeeded);
nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
nsCOMPtr<nsIFile> extra;
- return WriteExtraForMinidump(minidump, 0, Blacklist(), getter_AddRefs(extra));
+ return WriteExtraForMinidump(minidump, 0, /* content */ false,
+ getter_AddRefs(extra));
}
ThreadId
CurrentThreadId()
{
#if defined(XP_WIN)
return ::GetCurrentThreadId();
#elif defined(XP_LINUX)
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -8,16 +8,19 @@
// configured with --disable-crashreporter. If you add or remove a function
// from this header you must update both implementations otherwise you'll break
// builds that disable the crash reporter.
#ifndef nsExceptionHandler_h__
#define nsExceptionHandler_h__
#include "mozilla/Assertions.h"
+#include "mozilla/EnumeratedArray.h"
+
+#include "CrashAnnotations.h"
#include <functional>
#include <stddef.h>
#include <stdint.h>
#include "nsError.h"
#include "nsString.h"
#include "prio.h"
@@ -32,18 +35,16 @@
#include <mach/mach.h>
#endif
#if defined(XP_LINUX)
#include <signal.h>
#endif
class nsIFile;
-template<class KeyClass, class DataType> class nsDataHashtable;
-class nsCStringHashKey;
namespace CrashReporter {
/**
* Returns true if the crash reporter is using the dummy implementation.
*/
static inline bool
IsDummy() {
@@ -87,18 +88,21 @@ bool GetServerURL(nsACString& aServe
nsresult SetServerURL(const nsACString& aServerURL);
bool GetMinidumpPath(nsAString& aPath);
nsresult SetMinidumpPath(const nsAString& aPath);
// AnnotateCrashReport, RemoveCrashReportAnnotation and
// AppendAppNotesToCrashReport may be called from any thread in a chrome
// process, but may only be called from the main thread in a content process.
-nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data);
-nsresult RemoveCrashReportAnnotation(const nsACString& key);
+nsresult AnnotateCrashReport(Annotation key, bool data);
+nsresult AnnotateCrashReport(Annotation key, int data);
+nsresult AnnotateCrashReport(Annotation key, unsigned int data);
+nsresult AnnotateCrashReport(Annotation key, const nsACString& data);
+nsresult RemoveCrashReportAnnotation(Annotation key);
nsresult AppendAppNotesToCrashReport(const nsACString& data);
void AnnotateOOMAllocationSize(size_t size);
void AnnotateTexturesSize(size_t size);
nsresult SetGarbageCollecting(bool collecting);
void SetEventloopNestingLevel(uint32_t level);
void SetMinidumpAnalysisAllThreads();
@@ -108,17 +112,18 @@ nsresult SetupExtraData(nsIFile* aAppDat
// Registers an additional memory region to be included in the minidump
nsresult RegisterAppMemory(void* ptr, size_t length);
nsresult UnregisterAppMemory(void* ptr);
// Include heap regions of the crash context.
void SetIncludeContextHeap(bool aValue);
// Functions for working with minidumps and .extras
-typedef nsDataHashtable<nsCStringHashKey, nsCString> AnnotationTable;
+typedef mozilla::EnumeratedArray<Annotation, Annotation::Count, nsCString>
+ AnnotationTable;
void DeleteMinidumpFilesForID(const nsAString& id);
bool GetMinidumpForID(const nsAString& id, nsIFile** minidump);
bool GetIDFromMinidump(nsIFile* minidump, nsAString& id);
bool GetExtraFileForID(const nsAString& id, nsIFile** extraFile);
bool GetExtraFileForMinidump(nsIFile* minidump, nsIFile** extraFile);
bool AppendExtraData(const nsAString& id, const AnnotationTable& data);
bool AppendExtraData(nsIFile* extraFile, const AnnotationTable& data);
--- a/toolkit/crashreporter/test/unit/test_crash_abort.js
+++ b/toolkit/crashreporter/test/unit/test_crash_abort.js
@@ -1,15 +1,15 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
// Try crashing with an abort().
do_crash(function() {
crashType = CrashTestUtils.CRASH_ABORT;
- crashReporter.annotateCrashReport("TestKey", "TestValue");
+ crashReporter.annotateCrashReport(crashReporter.TestKey, "TestValue");
},
function(mdump, extra) {
Assert.equal(extra.TestKey, "TestValue");
},
// process will exit with a zero exit status
true);
}
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure.js
@@ -2,19 +2,19 @@ function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump("INFO | test_crash_after_js_large_allocation_failure.js | Can't test crashreporter in a non-libxul build.\n");
return;
}
do_crash(
function() {
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
- crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+ crashReporter.annotateCrashReport(crashReporter.TestKey, "Yes");
Cu.getJSTestingFunctions().reportLargeAllocationFailure();
Cu.forceGC();
},
function(mdump, extra) {
- Assert.equal(extra.TestingOOMCrash, "Yes");
+ Assert.equal(extra.TestKey, "Yes");
Assert.equal(false, "JSOutOfMemory" in extra);
Assert.equal(extra.JSLargeAllocationFailure, "Recovered");
},
true);
}
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure_reporting.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure_reporting.js
@@ -2,23 +2,23 @@ function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump("INFO | test_crash_after_js_oom_reporting.js | Can't test crashreporter in a non-libxul build.\n");
return;
}
do_crash(
function() {
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
- crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+ crashReporter.annotateCrashReport(crashReporter.TestKey, "Yes");
function crashWhileReporting() {
CrashTestUtils.crash(crashType);
}
Services.obs.addObserver(crashWhileReporting, "memory-pressure");
Cu.getJSTestingFunctions().reportLargeAllocationFailure();
},
function(mdump, extra) {
- Assert.equal(extra.TestingOOMCrash, "Yes");
+ Assert.equal(extra.TestKey, "Yes");
Assert.equal(extra.JSLargeAllocationFailure, "Reporting");
},
true);
}
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_oom_recovered.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_oom_recovered.js
@@ -2,18 +2,18 @@ function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump("INFO | test_crash_after_js_oom_recovered.js | Can't test crashreporter in a non-libxul build.\n");
return;
}
do_crash(
function() {
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
- crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+ crashReporter.annotateCrashReport(crashReporter.TestKey, "Yes");
Cu.getJSTestingFunctions().reportOutOfMemory();
Cu.forceGC();
},
function(mdump, extra) {
- Assert.equal(extra.TestingOOMCrash, "Yes");
+ Assert.equal(extra.TestKey, "Yes");
Assert.equal(extra.JSOutOfMemory, "Recovered");
},
true);
}
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported.js
@@ -2,26 +2,26 @@ function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump("INFO | test_crash_after_js_oom_reported.js | Can't test crashreporter in a non-libxul build.\n");
return;
}
do_crash(
function() {
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
- crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+ crashReporter.annotateCrashReport(crashReporter.TestKey, "Yes");
// GC now to avoid having it happen randomly later, which would make the
// test bogusly fail. See comment below.
Cu.forceGC();
Cu.getJSTestingFunctions().reportOutOfMemory();
},
function(mdump, extra) {
- Assert.equal(extra.TestingOOMCrash, "Yes");
+ Assert.equal(extra.TestKey, "Yes");
// The JSOutOfMemory field is absent if the JS engine never reported OOM,
// "Reported" if it did, and "Recovered" if it reported OOM but
// subsequently completed a full GC cycle. Since this test calls
// reportOutOfMemory() and then crashes, we expect "Reported".
//
// Theoretically, GC can happen any time, so it is just possible that
// this property could be "Recovered" even if the implementation is
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported_2.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported_2.js
@@ -2,23 +2,23 @@ function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump("INFO | test_crash_after_js_oom_reported_2.js | Can't test crashreporter in a non-libxul build.\n");
return;
}
do_crash(
function() {
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
- crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+ crashReporter.annotateCrashReport(crashReporter.TestKey, "Yes");
Cu.getJSTestingFunctions().reportOutOfMemory();
Cu.forceGC(); // recover from first OOM
Cu.getJSTestingFunctions().reportOutOfMemory();
},
function(mdump, extra) {
- Assert.equal(extra.TestingOOMCrash, "Yes");
+ Assert.equal(extra.TestKey, "Yes");
// Technically, GC can happen at any time, but it would be really
// peculiar for it to happen again heuristically right after a GC was
// forced. If extra.JSOutOfMemory is "Recovered" here, that's most
// likely a bug in the error reporting machinery.
Assert.equal(extra.JSOutOfMemory, "Reported");
},
true);
--- a/toolkit/crashreporter/test/unit/test_crash_moz_crash.js
+++ b/toolkit/crashreporter/test/unit/test_crash_moz_crash.js
@@ -1,13 +1,14 @@
function run_test() {
// Try crashing with a runtime abort
do_crash(function() {
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
- crashReporter.annotateCrashReport("TestKey", "TestValue");
+ crashReporter.annotateCrashReport(crashReporter.TestKey,
+ "TestValue");
},
function(mdump, extra) {
Assert.equal(extra.TestKey, "TestValue");
Assert.equal(false, "OOMAllocationSize" in extra);
Assert.equal(false, "JSOutOfMemory" in extra);
Assert.equal(false, "JSLargeAllocationFailure" in extra);
},
// process will exit with a zero exit status
--- a/toolkit/crashreporter/test/unit/test_crash_oom.js
+++ b/toolkit/crashreporter/test/unit/test_crash_oom.js
@@ -2,17 +2,17 @@ function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump("INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n");
return;
}
do_crash(
function() {
crashType = CrashTestUtils.CRASH_OOM;
- crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+ crashReporter.annotateCrashReport(crashReporter.TestKey, "Yes");
},
function(mdump, extra) {
- Assert.equal(extra.TestingOOMCrash, "Yes");
+ Assert.equal(extra.TestKey, "Yes");
Assert.ok("OOMAllocationSize" in extra);
Assert.ok(Number(extra.OOMAllocationSize) > 0);
},
true);
}
--- a/toolkit/crashreporter/test/unit/test_crash_purevirtual.js
+++ b/toolkit/crashreporter/test/unit/test_crash_purevirtual.js
@@ -8,16 +8,17 @@ function run_test() {
if (isOSX) {
dump("INFO | test_crash_purevirtual.js | TODO: purecalls not caught on OS X\n");
return;
}
// Try crashing with a pure virtual call
do_crash(function() {
crashType = CrashTestUtils.CRASH_PURE_VIRTUAL_CALL;
- crashReporter.annotateCrashReport("TestKey", "TestValue");
+ crashReporter.annotateCrashReport(crashReporter.TestKey,
+ "TestValue");
},
function(mdump, extra) {
Assert.equal(extra.TestKey, "TestValue");
},
// process will exit with a zero exit status
true);
}
--- a/toolkit/crashreporter/test/unit/test_crash_uncaught_exception.js
+++ b/toolkit/crashreporter/test/unit/test_crash_uncaught_exception.js
@@ -1,16 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
// Try crashing with an uncaught exception.
do_crash(function() {
crashType = CrashTestUtils.CRASH_UNCAUGHT_EXCEPTION;
- crashReporter.annotateCrashReport("TestKey", "TestValue");
+ crashReporter.annotateCrashReport(crashReporter.TestKey,
+ "TestValue");
},
function(mdump, extra) {
Assert.equal(extra.TestKey, "TestValue");
},
// process will exit with a zero exit status
true);
}
--- a/toolkit/crashreporter/test/unit/test_crashreporter.js
+++ b/toolkit/crashreporter/test/unit/test_crashreporter.js
@@ -34,43 +34,60 @@ function run_test() {
// check getting/setting minidumpPath
// it should be $TEMP by default, but I'm not sure if we can exactly test that
// this will at least test that it doesn't throw
Assert.notEqual(cr.minidumpPath.path, "");
var cwd = do_get_cwd();
cr.minidumpPath = cwd;
Assert.equal(cr.minidumpPath.path, cwd.path);
+ // Test annotateCrashReport()
try {
- cr.annotateCrashReport("equal=equal", "");
- do_throw("Calling annotateCrashReport() with an '=' in key should have thrown!");
+ cr.annotateCrashReport(undefined, "");
+ do_throw("Calling annotateCrashReport() with an undefined key should have thrown!");
} catch (ex) {
Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
try {
- cr.annotateCrashReport("new\nline", "");
- do_throw("Calling annotateCrashReport() with a '\\n' in key should have thrown!");
+ cr.annotateCrashReport(1000000, "");
+ do_throw("Calling annotateCrashReport() with a bogus key should have thrown!");
} catch (ex) {
Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
try {
- cr.annotateCrashReport("", "da\0ta");
+ cr.annotateCrashReport(cr.TestKey, "da\0ta");
do_throw("Calling annotateCrashReport() with a '\\0' in data should have thrown!");
} catch (ex) {
Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
- cr.annotateCrashReport("testKey", "testData1");
+ cr.annotateCrashReport(cr.TestKey, "testData1");
// Replace previous data.
- cr.annotateCrashReport("testKey", "testData2");
+ cr.annotateCrashReport(cr.TestKey, "testData2");
try {
cr.appendAppNotesToCrashReport("da\0ta");
do_throw("Calling appendAppNotesToCrashReport() with a '\\0' in data should have thrown!");
} catch (ex) {
Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
}
cr.appendAppNotesToCrashReport("additional testData3");
// Add more data.
cr.appendAppNotesToCrashReport("additional testData4");
+ // Test removeCrashReportAnnotation()
+ try {
+ cr.removeCrashReportAnnotation(undefined);
+ do_throw("Calling removeCrashReportAnnotation() with an undefined key should have thrown!");
+ } catch (ex) {
+ Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
+ }
+ try {
+ cr.removeCrashReportAnnotation(1000000);
+ do_throw("Calling removeCrashReportAnnotation() with a bogus key should have thrown!");
+ } catch (ex) {
+ Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
+ }
+ cr.removeCrashReportAnnotation(cr.TestKey);
+
+ // Testing setting the minidumpPath field
cr.minidumpPath = cwd;
Assert.equal(cr.minidumpPath.path, cwd.path);
}
--- a/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
+++ b/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
@@ -28,30 +28,37 @@ function run_test() {
}
if (is_win7_or_newer)
Assert.ok(CrashTestUtils.dumpHasStream(mdump.path, CrashTestUtils.MD_MEMORY_INFO_LIST_STREAM));
});
// check setting some basic data
do_crash(function() {
// Add various annotations
- crashReporter.annotateCrashReport("TestKey", "TestValue");
- crashReporter.annotateCrashReport("\u2665", "\u{1F4A9}");
+ crashReporter.annotateCrashReport(crashReporter.TestKey,
+ "TestValue");
+ crashReporter.annotateCrashReport(crashReporter.TestUnicode,
+ "\u{1F4A9}");
+ // Some annotations have an alternate form when written out, Addons
+ // for example will be written out as Add-ons
+ crashReporter.annotateCrashReport(crashReporter.Addons,
+ "test%40mozilla.org:0.1");
crashReporter.appendAppNotesToCrashReport("Junk");
crashReporter.appendAppNotesToCrashReport("MoreJunk");
// TelemetrySession setup will trigger the session annotation
let scope = {};
ChromeUtils.import("resource://gre/modules/TelemetryController.jsm", scope);
scope.TelemetryController.testSetup();
},
function(mdump, extra) {
Assert.equal(extra.TestKey, "TestValue");
- Assert.equal(extra["\u2665"], "\u{1F4A9}");
+ Assert.equal(extra.TestUnicode, "\u{1F4A9}");
Assert.equal(extra.Notes, "JunkMoreJunk");
+ Assert.equal(extra["Add-ons"], "test%40mozilla.org:0.1");
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
Assert.ok("TelemetrySessionId" in extra,
"The TelemetrySessionId field is present in the extra file");
Assert.ok(UUID_REGEX.test(extra.TelemetrySessionId),
"The TelemetrySessionId is a UUID");
Assert.ok(!("TelemetryClientId" in extra),
"The TelemetryClientId field is omitted by default");
Assert.ok(!("TelemetryServerURL" in extra),
--- a/toolkit/crashreporter/test/unit/test_event_files.js
+++ b/toolkit/crashreporter/test/unit/test_event_files.js
@@ -19,17 +19,18 @@ add_task(async function test_main_proces
let count = await new Promise((resolve, reject) => {
do_crash(
function() {
// TelemetrySession setup will trigger the session annotation
let scope = {};
ChromeUtils.import("resource://gre/modules/TelemetryController.jsm", scope);
scope.TelemetryController.testSetup();
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
- crashReporter.annotateCrashReport("ShutdownProgress", "event-test");
+ crashReporter.annotateCrashReport(crashReporter.ShutdownProgress,
+ "event-test");
},
(minidump, extra) => {
basename = minidump.leafName;
cm._eventsDirs = [getEventDir()];
cm.aggregateEventsFiles().then(resolve, reject);
},
true);
--- a/toolkit/crashreporter/test/unit/test_oom_annotation_windows.js
+++ b/toolkit/crashreporter/test/unit/test_oom_annotation_windows.js
@@ -2,20 +2,20 @@ function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump("INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n");
return;
}
do_crash(
function() {
crashType = CrashTestUtils.CRASH_OOM;
- crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+ crashReporter.annotateCrashReport(crashReporter.TestKey, "Yes");
},
function(mdump, extra) {
- Assert.equal(extra.TestingOOMCrash, "Yes");
+ Assert.equal(extra.TestKey, "Yes");
Assert.ok("OOMAllocationSize" in extra);
Assert.ok(Number(extra.OOMAllocationSize) > 0);
Assert.ok("SystemMemoryUsePercentage" in extra);
Assert.ok("TotalVirtualMemory" in extra);
Assert.ok("AvailableVirtualMemory" in extra);
Assert.ok("TotalPageFile" in extra);
Assert.ok("AvailablePageFile" in extra);
Assert.ok("TotalPhysicalMemory" in extra);
--- a/toolkit/crashreporter/test/unit_ipc/test_content_annotation.js
+++ b/toolkit/crashreporter/test/unit_ipc/test_content_annotation.js
@@ -5,17 +5,18 @@ function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump("INFO | test_content_annotation.js | Can't test crashreporter in a non-libxul build.\n");
return;
}
// Try crashing with a runtime abort
do_content_crash(function() {
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
- crashReporter.annotateCrashReport("TestKey", "TestValue");
+ crashReporter.annotateCrashReport(crashReporter.TestKey,
+ "TestValue");
crashReporter.appendAppNotesToCrashReport("!!!foo!!!");
},
function(mdump, extra) {
Assert.equal(extra.TestKey, "TestValue");
Assert.ok("StartupTime" in extra);
Assert.ok("ProcessType" in extra);
Assert.notEqual(extra.Notes.indexOf("!!!foo!!!"), -1);
});
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -250,16 +250,23 @@ static nsIProfileLock* gProfileLock;
int gRestartArgc;
char **gRestartArgv;
bool gIsGtest = false;
nsString gAbsoluteArgv0Path;
+// This should always be 0 and it's used to catch JavaScript invocations of
+// nsICrashReporter.annotateCrashReporter() that are passed an undefined value.
+// Since undefined values are translated into 0 we need to special-case it.
+static const uint32_t kInvalidAnnotation = 0;
+static const uint32_t kAnnotationCount =
+ static_cast<uint32_t>(CrashReporter::Annotation::Count);
+
#if defined(MOZ_WIDGET_GTK)
#include <glib.h>
#if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
#define CLEANUP_MEMORY 1
#define PANGO_ENABLE_BACKEND
#include <pango/pangofc-fontmap.h>
#endif
#include <gtk/gtk.h>
@@ -1329,20 +1336,53 @@ nsXULAppInfo::GetExtraFileForID(const ns
if (!CrashReporter::GetExtraFileForID(aId, aExtraFile)) {
return NS_ERROR_FILE_NOT_FOUND;
}
return NS_OK;
}
NS_IMETHODIMP
-nsXULAppInfo::AnnotateCrashReport(const nsACString& key,
+nsXULAppInfo::AnnotateCrashReport(uint32_t key,
const nsACString& data)
{
- return CrashReporter::AnnotateCrashReport(key, data);
+ if (key == kInvalidAnnotation || (key > kAnnotationCount)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return CrashReporter::AnnotateCrashReport(
+ static_cast<CrashReporter::Annotation>(key - 1), data);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::RemoveCrashReportAnnotation(uint32_t key)
+{
+ if (key == kInvalidAnnotation || (key > kAnnotationCount)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return CrashReporter::RemoveCrashReportAnnotation(
+ static_cast<CrashReporter::Annotation>(key - 1));
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::IsAnnotationWhitelistedForPing(const nsACString& aValue,
+ bool* aIsWhitelisted)
+{
+ nsAutoCString aValueStr(aValue);
+ CrashReporter::Annotation annotation;
+
+ if (CrashReporter::AnnotationFromString(annotation,
+ PromiseFlatCString(aValue).get())) {
+ *aIsWhitelisted = CrashReporter::IsAnnotationWhitelistedForPing(annotation);
+ } else {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::AppendAppNotesToCrashReport(const nsACString& data)
{
return CrashReporter::AppendAppNotesToCrashReport(data);
}
--- a/xpcom/system/moz.build
+++ b/xpcom/system/moz.build
@@ -1,21 +1,33 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
+GENERATED_FILES = [
+ 'nsICrashReporter.idl',
+]
+
XPIDL_SOURCES += [
+ '!nsICrashReporter.idl',
'nsIBlocklistService.idl',
- 'nsICrashReporter.idl',
'nsIDeviceSensors.idl',
'nsIGConfService.idl',
'nsIGeolocationProvider.idl',
'nsIGIOService.idl',
'nsIGSettingsService.idl',
'nsIHapticFeedback.idl',
'nsIPlatformInfo.idl',
'nsIXULAppInfo.idl',
'nsIXULRuntime.idl',
]
XPIDL_MODULE = 'xpcom_system'
+
+# Generate nsICrashReporter.idl
+annotations_idl = GENERATED_FILES['nsICrashReporter.idl']
+annotations_idl.script = '/toolkit/crashreporter/generate_crash_reporter_sources.py:emit_idl'
+annotations_idl.inputs = [
+ 'nsICrashReporter.idl.in',
+ '/toolkit/crashreporter/CrashAnnotations.yaml',
+]
rename from xpcom/system/nsICrashReporter.idl
rename to xpcom/system/nsICrashReporter.idl.in
--- a/xpcom/system/nsICrashReporter.idl
+++ b/xpcom/system/nsICrashReporter.idl.in
@@ -67,30 +67,55 @@ interface nsICrashReporter : nsISupports
* ID of the crash. Likely a UUID.
*
* @return The extra file associated with the ID.
*
* @throw NS_ERROR_FILE_NOT_FOUND if the extra file could not be found
*/
nsIFile getExtraFileForID(in AString id);
+${annotations}
+
/**
* Add some extra data to be submitted with a crash report.
*
* @param key
- * Name of the data to be added.
+ * One of the crash annotation constants.
* @param data
* Data to be added.
*
* @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
- * @throw NS_ERROR_INVALID_ARG if key or data contain invalid characters.
- * Invalid characters for key are '=' and
- * '\n'. Invalid character for data is '\0'.
+ * @throw NS_ERROR_INVALID_ARG if key contains an invalid value or data
+ * contains invalid characters. Invalid
+ * character for data is '\0'.
*/
- void annotateCrashReport(in AUTF8String key, in AUTF8String data);
+ void annotateCrashReport(in unsigned long key, in AUTF8String data);
+
+ /**
+ * Remove a crash report annotation.
+ *
+ * @param key
+ * One of the crash annotation constants.
+ *
+ * @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
+ * @throw NS_ERROR_INVALID_ARG if key contains an invalid value.
+ */
+ void removeCrashReportAnnotation(in unsigned long key);
+
+ /**
+ * Checks if an annotation is whitelisted for inclusion in the crash ping.
+ *
+ * @param key
+ * One of the crash annotation strings.
+ *
+ * @return True if the specified value is a valid annotation and can be
+ included in the crash ping, false otherwise.
+ * @throw NS_ERROR_INVALID_ARG if key contains an invalid value.
+ */
+ boolean isAnnotationWhitelistedForPing(in ACString value);
/**
* Append some data to the "Notes" field, to be submitted with a crash report.
* Unlike annotateCrashReport, this method will append to existing data.
*
* @param data
* Data to be added.
*
@@ -116,17 +141,17 @@ interface nsICrashReporter : nsISupports
/**
* Write a minidump immediately, with the user-supplied exception
* information. This is implemented on Windows only, because
* SEH (structured exception handling) exists on Windows only.
*
* @param aExceptionInfo EXCEPTION_INFO* provided by Window's SEH
*/
[noscript] void writeMinidumpForException(in voidPtr aExceptionInfo);
-
+
/**
* Append note containing an Obj-C exception's info.
*
* @param aException NSException object to append note for
*/
[noscript] void appendObjCExceptionInfoToAppNotes(in voidPtr aException);
/**