mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Backed out 3 changesets (bug 1293656) for unexpected crash dump failures in test_process_error.xul
Backed out changeset c8545ffbd0cb (bug 1293656) Backed out changeset f59861923f81 (bug 1293656) Backed out changeset b997bb5bccb4 (bug 1293656) MozReview-Commit-ID: GextDZheo78
This commit is contained in:
parent
b36c06e1aa
commit
c8e1b94ffa
@ -6,12 +6,10 @@
|
||||
|
||||
#include "CrashReporterHost.h"
|
||||
#include "CrashReporterMetadataShmem.h"
|
||||
#include "mozilla/LazyIdleThread.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
# include "nsIAsyncShutdown.h"
|
||||
# include "nsICrashService.h"
|
||||
#endif
|
||||
|
||||
@ -65,147 +63,6 @@ CrashReporterHost::GenerateCrashReport(RefPtr<nsIFile> aCrashDump)
|
||||
NotifyCrashService(mProcessType, dumpID, ¬es);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runnable used to execute the minidump analyzer program asynchronously after
|
||||
* a crash. This should run on a background thread not to block the main thread
|
||||
* over the potentially long minidump analysis. Once analysis is over, the
|
||||
* crash information will be relayed to the crash manager via another runnable
|
||||
* sent to the main thread. Shutdown will be blocked for the duration of the
|
||||
* entire process to ensure this information is sent.
|
||||
*/
|
||||
class AsyncMinidumpAnalyzer final : public nsIRunnable
|
||||
, public nsIAsyncShutdownBlocker
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a new minidump analyzer runnable, this will also block shutdown
|
||||
* until the associated crash has been added to the crash manager.
|
||||
*/
|
||||
AsyncMinidumpAnalyzer(int32_t aProcessType,
|
||||
int32_t aCrashType,
|
||||
const nsString& aChildDumpID)
|
||||
: mProcessType(aProcessType)
|
||||
, mCrashType(aCrashType)
|
||||
, mChildDumpID(aChildDumpID)
|
||||
, mName(NS_LITERAL_STRING("Crash reporter: blocking on minidump analysis"))
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv = GetShutdownBarrier()->AddBlocker(
|
||||
this, NS_LITERAL_STRING(__FILE__), __LINE__,
|
||||
NS_LITERAL_STRING("Minidump analysis"));
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (mProcessType == nsICrashService::PROCESS_TYPE_CONTENT) {
|
||||
CrashReporter::RunMinidumpAnalyzer(mChildDumpID);
|
||||
}
|
||||
|
||||
// Make a copy of these so we can copy them into the runnable lambda
|
||||
int32_t processType = mProcessType;
|
||||
int32_t crashType = mCrashType;
|
||||
nsString childDumpID(mChildDumpID);
|
||||
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction([=] () -> void {
|
||||
nsCOMPtr<nsICrashService> crashService =
|
||||
do_GetService("@mozilla.org/crashservice;1");
|
||||
if (crashService) {
|
||||
crashService->AddCrash(processType, crashType, childDumpID);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
|
||||
|
||||
if (barrier) {
|
||||
barrier->RemoveBlocker(this);
|
||||
}
|
||||
}));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aBarrierClient) override
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetName(nsAString& aName) override
|
||||
{
|
||||
aName = mName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetState(nsIPropertyBag**) override
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
private:
|
||||
~AsyncMinidumpAnalyzer() {}
|
||||
|
||||
// Returns the profile-before-change shutdown barrier
|
||||
static nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
|
||||
nsCOMPtr<nsIAsyncShutdownClient> barrier;
|
||||
nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return barrier.forget();
|
||||
}
|
||||
|
||||
int32_t mProcessType;
|
||||
int32_t mCrashType;
|
||||
const nsString mChildDumpID;
|
||||
const nsString mName;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(AsyncMinidumpAnalyzer, nsIRunnable, nsIAsyncShutdownBlocker)
|
||||
|
||||
/**
|
||||
* Add information about a crash to the crash manager. This method runs the
|
||||
* various activities required to gather additional information and notify the
|
||||
* crash manager asynchronously, since many of them involve I/O or potentially
|
||||
* long processing.
|
||||
*
|
||||
* @param aProcessType The type of process that crashed
|
||||
* @param aCrashType The type of crash (crash or hang)
|
||||
* @param aChildDumpID A string holding the ID of the dump associated with this
|
||||
* crash
|
||||
*/
|
||||
/* static */ void
|
||||
CrashReporterHost::AsyncAddCrash(int32_t aProcessType,
|
||||
int32_t aCrashType,
|
||||
const nsString& aChildDumpID)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
static StaticRefPtr<LazyIdleThread> sBackgroundThread;
|
||||
|
||||
if (!sBackgroundThread) {
|
||||
// Background thread used for running minidump analysis. It will be
|
||||
// destroyed immediately after it's done with the task.
|
||||
sBackgroundThread =
|
||||
new LazyIdleThread(0, NS_LITERAL_CSTRING("CrashReporterHost"));
|
||||
ClearOnShutdown(&sBackgroundThread);
|
||||
}
|
||||
|
||||
RefPtr<AsyncMinidumpAnalyzer> task =
|
||||
new AsyncMinidumpAnalyzer(aProcessType, aCrashType, aChildDumpID);
|
||||
|
||||
Unused << sBackgroundThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
|
||||
const nsString& aChildDumpID,
|
||||
@ -222,6 +79,12 @@ CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
|
||||
|
||||
MOZ_ASSERT(!aChildDumpID.IsEmpty());
|
||||
|
||||
nsCOMPtr<nsICrashService> crashService =
|
||||
do_GetService("@mozilla.org/crashservice;1");
|
||||
if (!crashService) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t processType;
|
||||
int32_t crashType = nsICrashService::CRASH_TYPE_CRASH;
|
||||
|
||||
@ -256,7 +119,7 @@ CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncAddCrash(processType, crashType, aChildDumpID);
|
||||
crashService->AddCrash(processType, crashType, aChildDumpID);
|
||||
Telemetry::Accumulate(Telemetry::SUBPROCESS_CRASHES_WITH_DUMP, telemetryKey, 1);
|
||||
}
|
||||
#endif
|
||||
|
@ -50,8 +50,6 @@ public:
|
||||
|
||||
private:
|
||||
void GenerateCrashReport(RefPtr<nsIFile> aCrashDump);
|
||||
static void AsyncAddCrash(int32_t aProcessType, int32_t aCrashType,
|
||||
const nsString& aChildDumpID);
|
||||
|
||||
private:
|
||||
GeckoProcessType mProcessType;
|
||||
|
@ -39,36 +39,6 @@ function dateToDays(date) {
|
||||
return Math.floor(date.getTime() / MILLISECONDS_IN_DAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the string stored in the specified field as JSON and then remove the
|
||||
* field from the object. The string might also be returned without parsing.
|
||||
*
|
||||
* @param obj {Object} The object holding the field
|
||||
* @param field {String} The name of the field to be parsed and removed
|
||||
* @param [parseAsJson=true] {Boolean} If true parse the field's contents as if
|
||||
* it were JSON code, otherwise return the rew string.
|
||||
*
|
||||
* @returns {Object|String} the parsed object or the raw string
|
||||
*/
|
||||
function parseAndRemoveField(obj, field, parseAsJson = true) {
|
||||
let value = null;
|
||||
|
||||
if (field in obj) {
|
||||
if (!parseAsJson) {
|
||||
value = obj[field];
|
||||
} else {
|
||||
try {
|
||||
value = JSON.parse(obj[field]);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
delete obj[field];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* A gateway to crash-related data.
|
||||
@ -144,9 +114,6 @@ this.CrashManager = function(options) {
|
||||
// Map of crash ID / promise tuples used to track adding new crashes.
|
||||
this._crashPromises = new Map();
|
||||
|
||||
// Promise for the crash ping used only for testing.
|
||||
this._pingPromise = null;
|
||||
|
||||
// The CrashStore currently attached to this object.
|
||||
this._store = null;
|
||||
|
||||
@ -207,41 +174,6 @@ this.CrashManager.prototype = Object.freeze({
|
||||
// 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",
|
||||
"ProductID",
|
||||
"ProductName",
|
||||
"ReleaseChannel",
|
||||
"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.
|
||||
*
|
||||
@ -452,12 +384,7 @@ this.CrashManager.prototype = Object.freeze({
|
||||
this._crashPromises.delete(id);
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
// Send a telemetry ping for each content process crash
|
||||
if (processType === this.PROCESS_TYPE_CONTENT) {
|
||||
this._sendCrashPing(id, processType, date, metadata);
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
return promise;
|
||||
},
|
||||
@ -617,51 +544,6 @@ this.CrashManager.prototype = Object.freeze({
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_filterAnnotations: function(annotations) {
|
||||
let filteredAnnotations = {};
|
||||
|
||||
for (let line in annotations) {
|
||||
if (this.ANNOTATION_WHITELIST.includes(line)) {
|
||||
filteredAnnotations[line] = annotations[line];
|
||||
}
|
||||
}
|
||||
|
||||
return filteredAnnotations;
|
||||
},
|
||||
|
||||
_sendCrashPing: function(crashId, type, date, metadata = {}) {
|
||||
// If we have a saved environment, use it. Otherwise report
|
||||
// the current environment.
|
||||
let reportMeta = Cu.cloneInto(metadata, myScope);
|
||||
let crashEnvironment = parseAndRemoveField(reportMeta,
|
||||
"TelemetryEnvironment");
|
||||
let sessionId = parseAndRemoveField(reportMeta, "TelemetrySessionId",
|
||||
/* parseAsJson */ false);
|
||||
let stackTraces = parseAndRemoveField(reportMeta, "StackTraces");
|
||||
|
||||
// Filter the remaining annotations to remove privacy-sensitive ones
|
||||
reportMeta = this._filterAnnotations(reportMeta);
|
||||
|
||||
this._pingPromise = TelemetryController.submitExternalPing("crash",
|
||||
{
|
||||
version: 1,
|
||||
crashDate: date.toISOString().slice(0, 10), // YYYY-MM-DD
|
||||
sessionId: sessionId,
|
||||
crashId: crashId,
|
||||
processType: type,
|
||||
stackTraces: stackTraces,
|
||||
metadata: reportMeta,
|
||||
hasCrashEnvironment: (crashEnvironment !== null),
|
||||
},
|
||||
{
|
||||
retentionDays: 180,
|
||||
addClientId: true,
|
||||
addEnvironment: true,
|
||||
overrideEnvironment: crashEnvironment,
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
_handleEventFilePayload: function(store, entry, type, date, payload) {
|
||||
// The payload types and formats are documented in docs/crash-events.rst.
|
||||
// Do not change the format of an existing type. Instead, invent a new
|
||||
@ -683,7 +565,48 @@ this.CrashManager.prototype = Object.freeze({
|
||||
store.addCrash(this.PROCESS_TYPE_MAIN, this.CRASH_TYPE_CRASH,
|
||||
crashID, date, metadata);
|
||||
|
||||
this._sendCrashPing(crashID, this.PROCESS_TYPE_MAIN, date, metadata);
|
||||
// If we have a saved environment, use it. Otherwise report
|
||||
// the current environment.
|
||||
let crashEnvironment = null;
|
||||
let sessionId = null;
|
||||
let stackTraces = null;
|
||||
let reportMeta = Cu.cloneInto(metadata, myScope);
|
||||
if ('TelemetryEnvironment' in reportMeta) {
|
||||
try {
|
||||
crashEnvironment = JSON.parse(reportMeta.TelemetryEnvironment);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
delete reportMeta.TelemetryEnvironment;
|
||||
}
|
||||
if ('TelemetrySessionId' in reportMeta) {
|
||||
sessionId = reportMeta.TelemetrySessionId;
|
||||
delete reportMeta.TelemetrySessionId;
|
||||
}
|
||||
if ('StackTraces' in reportMeta) {
|
||||
try {
|
||||
stackTraces = JSON.parse(reportMeta.StackTraces);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
delete reportMeta.StackTraces;
|
||||
}
|
||||
TelemetryController.submitExternalPing("crash",
|
||||
{
|
||||
version: 1,
|
||||
crashDate: date.toISOString().slice(0, 10), // YYYY-MM-DD
|
||||
sessionId: sessionId,
|
||||
crashId: entry.id,
|
||||
stackTraces: stackTraces,
|
||||
metadata: reportMeta,
|
||||
hasCrashEnvironment: (crashEnvironment !== null),
|
||||
},
|
||||
{
|
||||
retentionDays: 180,
|
||||
addClientId: true,
|
||||
addEnvironment: true,
|
||||
overrideEnvironment: crashEnvironment,
|
||||
});
|
||||
break;
|
||||
|
||||
case "crash.submission.1":
|
||||
|
@ -6,44 +6,9 @@
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/AsyncShutdown.jsm", this);
|
||||
Cu.import("resource://gre/modules/KeyValueParser.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
|
||||
/**
|
||||
* Process the .extra file associated with the crash id and return the
|
||||
* annotations it contains in an object.
|
||||
*
|
||||
* @param crashID {string} Crash ID. Likely a UUID.
|
||||
*
|
||||
* @return {Promise} A promise that resolves to an object holding the crash
|
||||
* annotations, this object may be empty if no annotations were found.
|
||||
*/
|
||||
function processExtraFile(id) {
|
||||
let crDir = OS.Path.join(OS.Constants.Path.userApplicationDataDir,
|
||||
"Crash Reports");
|
||||
let pendingDumpsDir = OS.Path.join(crDir, "pending");
|
||||
let extraPath = OS.Path.join(pendingDumpsDir, id + ".extra");
|
||||
|
||||
return Task.spawn(function* () {
|
||||
try {
|
||||
let decoder = new TextDecoder();
|
||||
let extraFile = yield OS.File.read(extraPath);
|
||||
let extraData = decoder.decode(extraFile);
|
||||
|
||||
return parseKeyValuePairs(extraData);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This component makes crash data available throughout the application.
|
||||
*
|
||||
@ -90,13 +55,7 @@ CrashService.prototype = Object.freeze({
|
||||
throw new Error("Unrecognized CRASH_TYPE: " + crashType);
|
||||
}
|
||||
|
||||
AsyncShutdown.profileBeforeChange.addBlocker(
|
||||
"CrashService waiting for content crash ping to be sent",
|
||||
processExtraFile(id).then(metadata => {
|
||||
return Services.crashmanager.addCrash(processType, crashType, id,
|
||||
new Date(), metadata)
|
||||
})
|
||||
);
|
||||
Services.crashmanager.addCrash(processType, crashType, id, new Date());
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
|
@ -15,12 +15,6 @@ From JavaScript, the service can be accessed via::
|
||||
That will give you an instance of ``CrashManager`` from ``CrashManager.jsm``.
|
||||
From there, you can access and manipulate crash data.
|
||||
|
||||
The crash manager stores statistical information about crashes as well as
|
||||
detailed information for both browser and content crashes. The crash manager
|
||||
automatically detects new browser crashes at startup by scanning for
|
||||
:ref:`crash-events`. Content process crash information on the other hand is
|
||||
provided externally.
|
||||
|
||||
Other Documents
|
||||
===============
|
||||
|
||||
|
@ -209,59 +209,48 @@ add_task(function* test_schedule_maintenance() {
|
||||
Assert.equal(crashes[0].id, "id1");
|
||||
});
|
||||
|
||||
const crashId = "3cb67eba-0dc7-6f78-6a569a0e-172287ec";
|
||||
const productName = "Firefox";
|
||||
const productId = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
|
||||
const stackTraces = "{\"status\":\"OK\"}";
|
||||
|
||||
add_task(function* test_main_crash_event_file() {
|
||||
let ac = new TelemetryArchiveTesting.Checker();
|
||||
yield ac.promiseInit();
|
||||
let theEnvironment = TelemetryEnvironment.currentEnvironment;
|
||||
const sessionId = "be66af2f-2ee5-4330-ae95-44462dfbdf0c";
|
||||
let sessionId = "be66af2f-2ee5-4330-ae95-44462dfbdf0c";
|
||||
let stackTraces = { status: "OK" };
|
||||
|
||||
// To test proper escaping, add data to the environment with an embedded
|
||||
// double-quote
|
||||
theEnvironment.testValue = "MyValue\"";
|
||||
|
||||
let m = yield getManager();
|
||||
const fileContent = crashId + "\n" +
|
||||
"ProductName=" + productName + "\n" +
|
||||
"ProductID=" + productId + "\n" +
|
||||
const fileContent = "id1\nk1=v1\nk2=v2\n" +
|
||||
"TelemetryEnvironment=" + JSON.stringify(theEnvironment) + "\n" +
|
||||
"TelemetrySessionId=" + sessionId + "\n" +
|
||||
"StackTraces=" + stackTraces + "\n" +
|
||||
"ThisShouldNot=end-up-in-the-ping\n";
|
||||
"StackTraces=" + JSON.stringify(stackTraces) + "\n";
|
||||
|
||||
yield m.createEventsFile(crashId, "crash.main.2", DUMMY_DATE, fileContent);
|
||||
yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, fileContent);
|
||||
let count = yield m.aggregateEventsFiles();
|
||||
Assert.equal(count, 1);
|
||||
|
||||
let crashes = yield m.getCrashes();
|
||||
Assert.equal(crashes.length, 1);
|
||||
Assert.equal(crashes[0].id, crashId);
|
||||
Assert.equal(crashes[0].id, "id1");
|
||||
Assert.equal(crashes[0].type, "main-crash");
|
||||
Assert.equal(crashes[0].metadata.ProductName, productName);
|
||||
Assert.equal(crashes[0].metadata.ProductID, productId);
|
||||
Assert.equal(crashes[0].metadata.k1, "v1");
|
||||
Assert.equal(crashes[0].metadata.k2, "v2");
|
||||
Assert.ok(crashes[0].metadata.TelemetryEnvironment);
|
||||
Assert.equal(Object.getOwnPropertyNames(crashes[0].metadata).length, 6);
|
||||
Assert.equal(Object.getOwnPropertyNames(crashes[0].metadata).length, 5);
|
||||
Assert.equal(crashes[0].metadata.TelemetrySessionId, sessionId);
|
||||
Assert.ok(crashes[0].metadata.StackTraces);
|
||||
Assert.deepEqual(crashes[0].crashDate, DUMMY_DATE);
|
||||
|
||||
let found = yield ac.promiseFindPing("crash", [
|
||||
[["payload", "hasCrashEnvironment"], true],
|
||||
[["payload", "metadata", "ProductName"], productName],
|
||||
[["payload", "metadata", "ProductID"], productId],
|
||||
[["payload", "crashId"], crashId],
|
||||
[["payload", "metadata", "k1"], "v1"],
|
||||
[["payload", "crashId"], "1"],
|
||||
[["payload", "stackTraces", "status"], "OK"],
|
||||
[["payload", "sessionId"], sessionId],
|
||||
]);
|
||||
Assert.ok(found, "Telemetry ping submitted for found crash");
|
||||
Assert.deepEqual(found.environment, theEnvironment,
|
||||
"The saved environment should be present");
|
||||
Assert.equal(found.payload.metadata.ThisShouldNot, undefined,
|
||||
"Non-whitelisted fields should be filtered out");
|
||||
Assert.deepEqual(found.environment, theEnvironment, "The saved environment should be present");
|
||||
|
||||
count = yield m.aggregateEventsFiles();
|
||||
Assert.equal(count, 0);
|
||||
@ -270,29 +259,22 @@ add_task(function* test_main_crash_event_file() {
|
||||
add_task(function* test_main_crash_event_file_noenv() {
|
||||
let ac = new TelemetryArchiveTesting.Checker();
|
||||
yield ac.promiseInit();
|
||||
const fileContent = crashId + "\n" +
|
||||
"ProductName=" + productName + "\n" +
|
||||
"ProductID=" + productId + "\n";
|
||||
|
||||
let m = yield getManager();
|
||||
yield m.createEventsFile(crashId, "crash.main.2", DUMMY_DATE, fileContent);
|
||||
yield m.createEventsFile("1", "crash.main.2", DUMMY_DATE, "id1\nk1=v3\nk2=v2");
|
||||
let count = yield m.aggregateEventsFiles();
|
||||
Assert.equal(count, 1);
|
||||
|
||||
let crashes = yield m.getCrashes();
|
||||
Assert.equal(crashes.length, 1);
|
||||
Assert.equal(crashes[0].id, crashId);
|
||||
Assert.equal(crashes[0].id, "id1");
|
||||
Assert.equal(crashes[0].type, "main-crash");
|
||||
Assert.deepEqual(crashes[0].metadata, {
|
||||
ProductName: productName,
|
||||
ProductID: productId
|
||||
});
|
||||
Assert.deepEqual(crashes[0].metadata, { k1: "v3", k2: "v2"});
|
||||
Assert.deepEqual(crashes[0].crashDate, DUMMY_DATE);
|
||||
|
||||
let found = yield ac.promiseFindPing("crash", [
|
||||
[["payload", "hasCrashEnvironment"], false],
|
||||
[["payload", "metadata", "ProductName"], productName],
|
||||
[["payload", "metadata", "ProductID"], productId],
|
||||
[["payload", "metadata", "k1"], "v3"],
|
||||
]);
|
||||
Assert.ok(found, "Telemetry ping submitted for found crash");
|
||||
Assert.ok(found.environment, "There is an environment");
|
||||
@ -458,28 +440,6 @@ add_task(function* test_addCrash() {
|
||||
Assert.ok(crash.isOfType(m.PROCESS_TYPE_CONTENT, m.CRASH_TYPE_HANG));
|
||||
});
|
||||
|
||||
add_task(function* test_content_crash_ping() {
|
||||
let ac = new TelemetryArchiveTesting.Checker();
|
||||
yield ac.promiseInit();
|
||||
|
||||
let m = yield getManager();
|
||||
let id = yield m.createDummyDump();
|
||||
yield m.addCrash(m.PROCESS_TYPE_CONTENT, m.CRASH_TYPE_CRASH, id, DUMMY_DATE, {
|
||||
StackTraces: stackTraces,
|
||||
ThisShouldNot: "end-up-in-the-ping"
|
||||
});
|
||||
yield m._pingPromise;
|
||||
|
||||
let found = yield ac.promiseFindPing("crash", [
|
||||
[["payload", "crashId"], id],
|
||||
[["payload", "processType"], m.PROCESS_TYPE_CONTENT],
|
||||
[["payload", "stackTraces", "status"], "OK"],
|
||||
]);
|
||||
Assert.ok(found, "Telemetry ping submitted for content crash");
|
||||
Assert.equal(found.payload.metadata.ThisShouldNot, undefined,
|
||||
"Non-whitelisted fields should be filtered out");
|
||||
});
|
||||
|
||||
add_task(function* test_generateSubmissionID() {
|
||||
let m = yield getManager();
|
||||
|
||||
|
@ -130,15 +130,6 @@ appropriate crash annotations specific to the crash. All child-process
|
||||
crashes are annotated with a ``ProcessType`` annotation, such as "content" or
|
||||
"plugin".
|
||||
|
||||
Once the minidump file has been generated the
|
||||
``mozilla::dom::CrashReporterHost`` is notified of the crash. It will first
|
||||
try to extract the stack traces from the minidump file using the
|
||||
*minidump analyzer*. Then the stack traces will be stored in the extra file
|
||||
together with the rest of the crash annotations and finally the crash will be
|
||||
recorded by calling ```CrashService.addCrash()```. This last step adds the
|
||||
crash to the ```CrashManager``` database and automatically sends a crash ping
|
||||
with information about the crash.
|
||||
|
||||
Submission of child process crashes is handled by application code. This
|
||||
code prompts the user to submit crashes in context-appropriate UI and then
|
||||
submits the crashes using ``CrashSubmit.jsm``.
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryService.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/dom/CrashReporterChild.h"
|
||||
@ -130,7 +129,6 @@ typedef std::wstring xpstring;
|
||||
#define XP_STRLEN(x) wcslen(x)
|
||||
#define my_strlen strlen
|
||||
#define CRASH_REPORTER_FILENAME "crashreporter.exe"
|
||||
#define MINIDUMP_ANALYZER_FILENAME "minidump-analyzer.exe"
|
||||
#define PATH_SEPARATOR "\\"
|
||||
#define XP_PATH_SEPARATOR L"\\"
|
||||
#define XP_PATH_SEPARATOR_CHAR L'\\'
|
||||
@ -149,7 +147,6 @@ typedef std::string xpstring;
|
||||
#define XP_TEXT(x) x
|
||||
#define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
|
||||
#define CRASH_REPORTER_FILENAME "crashreporter"
|
||||
#define MINIDUMP_ANALYZER_FILENAME "minidump-analyzer"
|
||||
#define PATH_SEPARATOR "/"
|
||||
#define XP_PATH_SEPARATOR "/"
|
||||
#define XP_PATH_SEPARATOR_CHAR '/'
|
||||
@ -186,6 +183,34 @@ static const XP_CHAR extraFileExtension[] = XP_TEXT(".extra");
|
||||
static const XP_CHAR memoryReportExtension[] = XP_TEXT(".memory.json.gz");
|
||||
static xpstring *defaultMemoryReportPath = nullptr;
|
||||
|
||||
// A whitelist of crash annotations which do not contain sensitive data
|
||||
// and are saved in the crash record and sent with Firefox Health Report.
|
||||
static char const * const kCrashEventAnnotations[] = {
|
||||
"AsyncShutdownTimeout",
|
||||
"BuildID",
|
||||
"ProductID",
|
||||
"ProductName",
|
||||
"ReleaseChannel",
|
||||
"SecondsSinceLastCrash",
|
||||
"ShutdownProgress",
|
||||
"StartupCrash",
|
||||
"TelemetryEnvironment",
|
||||
"Version",
|
||||
// The following entries are not normal annotations but are included
|
||||
// in the crash record/FHR:
|
||||
// "ContainsMemoryReport"
|
||||
// "EventLoopNestingLevel"
|
||||
// "IsGarbageCollecting"
|
||||
// "AvailablePageFile"
|
||||
// "AvailableVirtualMemory"
|
||||
// "SystemMemoryUsePercentage"
|
||||
// "OOMAllocationSize"
|
||||
// "TotalPageFile"
|
||||
// "TotalPhysicalMemory"
|
||||
// "TotalVirtualMemory"
|
||||
// "MozCrashReason"
|
||||
};
|
||||
|
||||
static const char kCrashMainID[] = "crash.main.2\n";
|
||||
|
||||
static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
|
||||
@ -193,9 +218,6 @@ static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
|
||||
static XP_CHAR* pendingDirectory;
|
||||
static XP_CHAR* crashReporterPath;
|
||||
static XP_CHAR* memoryReportPath;
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
static XP_CHAR* minidumpAnalyzerPath;
|
||||
#endif // !defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
// Where crash events should go.
|
||||
static XP_CHAR* eventsDirectory;
|
||||
@ -529,12 +551,10 @@ CreatePathFromFile(nsIFile* file)
|
||||
#endif
|
||||
|
||||
static XP_CHAR*
|
||||
Concat(XP_CHAR* str, const XP_CHAR* toAppend, size_t* size)
|
||||
Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
|
||||
{
|
||||
size_t appendLen = XP_STRLEN(toAppend);
|
||||
if (appendLen >= *size) {
|
||||
appendLen = *size - 1;
|
||||
}
|
||||
int appendLen = XP_STRLEN(toAppend);
|
||||
if (appendLen >= *size) appendLen = *size - 1;
|
||||
|
||||
memcpy(str, toAppend, appendLen * sizeof(XP_CHAR));
|
||||
str += appendLen;
|
||||
@ -754,7 +774,7 @@ OpenAPIData(PlatformWriter& aWriter,
|
||||
)
|
||||
{
|
||||
static XP_CHAR extraDataPath[XP_PATH_MAX];
|
||||
size_t size = XP_PATH_MAX;
|
||||
int size = XP_PATH_MAX;
|
||||
XP_CHAR* p;
|
||||
if (minidump_id) {
|
||||
p = Concat(extraDataPath, dump_path, &size);
|
||||
@ -805,136 +825,6 @@ WriteGlobalMemoryStatus(PlatformWriter* apiData, PlatformWriter* eventFile)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
/**
|
||||
* Launches the program specified in aProgramPath with aMinidumpPath as its
|
||||
* sole argument.
|
||||
*
|
||||
* @param aProgramPath The path of the program to be launched
|
||||
* @param aMinidumpPath The path of the minidump file, passed as an argument
|
||||
* to the launched program
|
||||
*/
|
||||
static bool
|
||||
LaunchProgram(XP_CHAR* aProgramPath, XP_CHAR* aMinidumpPath)
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
XP_CHAR cmdLine[CMDLINE_SIZE];
|
||||
XP_CHAR* p;
|
||||
|
||||
size_t size = CMDLINE_SIZE;
|
||||
p = Concat(cmdLine, L"\"", &size);
|
||||
p = Concat(p, aProgramPath, &size);
|
||||
p = Concat(p, L"\" \"", &size);
|
||||
p = Concat(p, aMinidumpPath, &size);
|
||||
Concat(p, L"\"", &size);
|
||||
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
// If CreateProcess() fails don't do anything
|
||||
if (CreateProcess(nullptr, (LPWSTR)cmdLine, nullptr, nullptr, FALSE,
|
||||
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
|
||||
nullptr, nullptr, &si, &pi)) {
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
#elif defined(XP_UNIX)
|
||||
#ifdef XP_MACOSX
|
||||
pid_t pid = 0;
|
||||
char* const my_argv[] = {
|
||||
aProgramPath,
|
||||
aMinidumpPath,
|
||||
nullptr
|
||||
};
|
||||
|
||||
char **env = nullptr;
|
||||
char ***nsEnv = _NSGetEnviron();
|
||||
if (nsEnv)
|
||||
env = *nsEnv;
|
||||
|
||||
int rv = posix_spawnp(&pid, my_argv[0], nullptr, &spawnattr, my_argv, env);
|
||||
|
||||
if (rv != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#else // !XP_MACOSX
|
||||
pid_t pid = sys_fork();
|
||||
|
||||
if (pid == -1) {
|
||||
return false;
|
||||
} else if (pid == 0) {
|
||||
// need to clobber this, as libcurl might load NSS,
|
||||
// and we want it to load the system NSS.
|
||||
unsetenv("LD_LIBRARY_PATH");
|
||||
Unused << execl(aProgramPath,
|
||||
aProgramPath, aMinidumpPath, (char*)0);
|
||||
_exit(1);
|
||||
}
|
||||
#endif // XP_MACOSX
|
||||
#endif // XP_UNIX
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* Launch the crash reporter activity on Android
|
||||
*
|
||||
* @param aProgramPath The path of the program to be launched
|
||||
* @param aMinidumpPath The path to the crash minidump file
|
||||
* @param aSucceeded True if the minidump was obtained successfully
|
||||
*/
|
||||
|
||||
static bool
|
||||
LaunchCrashReporterActivity(XP_CHAR* aProgramPath, XP_CHAR* aMinidumpPath,
|
||||
bool aSucceeded)
|
||||
{
|
||||
pid_t pid = sys_fork();
|
||||
|
||||
if (pid == -1)
|
||||
return false;
|
||||
else if (pid == 0) {
|
||||
// Invoke the reportCrash activity using am
|
||||
if (androidUserSerial) {
|
||||
Unused << execlp("/system/bin/am",
|
||||
"/system/bin/am",
|
||||
"start",
|
||||
"--user", androidUserSerial,
|
||||
"-a", "org.mozilla.gecko.reportCrash",
|
||||
"-n", aProgramPath,
|
||||
"--es", "minidumpPath", aMinidumpPath,
|
||||
"--ez", "minidumpSuccess", aSucceeded ? "true" : "false",
|
||||
(char*)0);
|
||||
} else {
|
||||
Unused << execlp("/system/bin/am",
|
||||
"/system/bin/am",
|
||||
"start",
|
||||
"-a", "org.mozilla.gecko.reportCrash",
|
||||
"-n", aProgramPath,
|
||||
"--es", "minidumpPath", aMinidumpPath,
|
||||
"--ez", "minidumpSuccess", aSucceeded ? "true" : "false",
|
||||
(char*)0);
|
||||
}
|
||||
_exit(1);
|
||||
|
||||
} else {
|
||||
// We need to wait on the 'am start' command above to finish, otherwise everything will
|
||||
// be killed by the ActivityManager as soon as the signal handler exits
|
||||
int status;
|
||||
Unused << HANDLE_EINTR(sys_waitpid(pid, &status, __WALL));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool MinidumpCallback(
|
||||
#ifdef XP_LINUX
|
||||
const MinidumpDescriptor& descriptor,
|
||||
@ -952,7 +842,7 @@ bool MinidumpCallback(
|
||||
bool returnValue = showOSCrashReporter ? false : succeeded;
|
||||
|
||||
static XP_CHAR minidumpPath[XP_PATH_MAX];
|
||||
size_t size = XP_PATH_MAX;
|
||||
int size = XP_PATH_MAX;
|
||||
XP_CHAR* p;
|
||||
#ifndef XP_LINUX
|
||||
p = Concat(minidumpPath, dump_path, &size);
|
||||
@ -1074,7 +964,7 @@ bool MinidumpCallback(
|
||||
|
||||
if (eventsDirectory) {
|
||||
static XP_CHAR crashEventPath[XP_PATH_MAX];
|
||||
size_t size = XP_PATH_MAX;
|
||||
int size = XP_PATH_MAX;
|
||||
XP_CHAR* p;
|
||||
p = Concat(crashEventPath, eventsDirectory, &size);
|
||||
p = Concat(p, XP_PATH_SEPARATOR, &size);
|
||||
@ -1120,8 +1010,10 @@ bool MinidumpCallback(
|
||||
timeSinceLastCrashString);
|
||||
}
|
||||
if (isGarbageCollecting) {
|
||||
WriteAnnotation(apiData, "IsGarbageCollecting", "1");
|
||||
WriteAnnotation(eventFile, "IsGarbageCollecting", "1");
|
||||
WriteAnnotation(apiData, "IsGarbageCollecting",
|
||||
isGarbageCollecting ? "1" : "0");
|
||||
WriteAnnotation(eventFile, "IsGarbageCollecting",
|
||||
isGarbageCollecting ? "1" : "0");
|
||||
}
|
||||
|
||||
char buffer[128];
|
||||
@ -1187,22 +1079,108 @@ bool MinidumpCallback(
|
||||
}
|
||||
}
|
||||
|
||||
if (!doReport) {
|
||||
#ifdef XP_WIN
|
||||
if (!doReport) {
|
||||
TerminateProcess(GetCurrentProcess(), 1);
|
||||
#endif // XP_WIN
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID) // Android
|
||||
returnValue = LaunchCrashReporterActivity(crashReporterPath, minidumpPath,
|
||||
succeeded);
|
||||
#else // Windows, Mac, Linux, etc...
|
||||
returnValue = LaunchProgram(crashReporterPath, minidumpPath);
|
||||
#ifdef XP_WIN
|
||||
XP_CHAR cmdLine[CMDLINE_SIZE];
|
||||
size = CMDLINE_SIZE;
|
||||
p = Concat(cmdLine, L"\"", &size);
|
||||
p = Concat(p, crashReporterPath, &size);
|
||||
p = Concat(p, L"\" \"", &size);
|
||||
p = Concat(p, minidumpPath, &size);
|
||||
Concat(p, L"\"", &size);
|
||||
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||
si.wShowWindow = SW_SHOWNORMAL;
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
if (CreateProcess(nullptr, (LPWSTR)cmdLine, nullptr, nullptr, FALSE, 0,
|
||||
nullptr, nullptr, &si, &pi)) {
|
||||
CloseHandle( pi.hProcess );
|
||||
CloseHandle( pi.hThread );
|
||||
}
|
||||
// we're not really in a position to do anything if the CreateProcess fails
|
||||
TerminateProcess(GetCurrentProcess(), 1);
|
||||
#elif defined(XP_UNIX)
|
||||
if (!doReport) {
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
char* const my_argv[] = {
|
||||
crashReporterPath,
|
||||
minidumpPath,
|
||||
nullptr
|
||||
};
|
||||
|
||||
char **env = nullptr;
|
||||
char ***nsEnv = _NSGetEnviron();
|
||||
if (nsEnv)
|
||||
env = *nsEnv;
|
||||
int result = posix_spawnp(nullptr,
|
||||
my_argv[0],
|
||||
nullptr,
|
||||
&spawnattr,
|
||||
my_argv,
|
||||
env);
|
||||
|
||||
if (result != 0)
|
||||
return false;
|
||||
|
||||
#else // !XP_MACOSX
|
||||
pid_t pid = sys_fork();
|
||||
|
||||
if (pid == -1)
|
||||
return false;
|
||||
else if (pid == 0) {
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
// need to clobber this, as libcurl might load NSS,
|
||||
// and we want it to load the system NSS.
|
||||
unsetenv("LD_LIBRARY_PATH");
|
||||
Unused << execl(crashReporterPath,
|
||||
crashReporterPath, minidumpPath, (char*)0);
|
||||
#else
|
||||
// Invoke the reportCrash activity using am
|
||||
if (androidUserSerial) {
|
||||
Unused << execlp("/system/bin/am",
|
||||
"/system/bin/am",
|
||||
"start",
|
||||
"--user", androidUserSerial,
|
||||
"-a", "org.mozilla.gecko.reportCrash",
|
||||
"-n", crashReporterPath,
|
||||
"--es", "minidumpPath", minidumpPath,
|
||||
"--ez", "minidumpSuccess", succeeded ? "true" : "false",
|
||||
(char*)0);
|
||||
} else {
|
||||
Unused << execlp("/system/bin/am",
|
||||
"/system/bin/am",
|
||||
"start",
|
||||
"-a", "org.mozilla.gecko.reportCrash",
|
||||
"-n", crashReporterPath,
|
||||
"--es", "minidumpPath", minidumpPath,
|
||||
"--ez", "minidumpSuccess", succeeded ? "true" : "false",
|
||||
(char*)0);
|
||||
}
|
||||
#endif
|
||||
_exit(1);
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
} else {
|
||||
// We need to wait on the 'am start' command above to finish, otherwise everything will
|
||||
// be killed by the ActivityManager as soon as the signal handler exits
|
||||
int status;
|
||||
Unused << HANDLE_EINTR(sys_waitpid(pid, &status, __WALL));
|
||||
#endif
|
||||
}
|
||||
#endif // XP_MACOSX
|
||||
#endif // XP_UNIX
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
@ -1277,7 +1255,7 @@ BuildTempPath(char* aBuf, size_t aBufLen)
|
||||
if (!tempenv) {
|
||||
return false;
|
||||
}
|
||||
size_t size = aBufLen;
|
||||
int size = (int)aBufLen;
|
||||
Concat(aBuf, tempenv, &size);
|
||||
return EnsureTrailingSlash(aBuf, aBufLen);
|
||||
}
|
||||
@ -1292,7 +1270,7 @@ BuildTempPath(char* aBuf, size_t aBufLen)
|
||||
if (!tempenv) {
|
||||
tempenv = tmpPath;
|
||||
}
|
||||
size_t size = aBufLen;
|
||||
int size = (int)aBufLen;
|
||||
Concat(aBuf, tempenv, &size);
|
||||
return EnsureTrailingSlash(aBuf, aBufLen);
|
||||
}
|
||||
@ -1329,7 +1307,7 @@ PrepareChildExceptionTimeAnnotations()
|
||||
static XP_CHAR tempPath[XP_PATH_MAX] = {0};
|
||||
|
||||
// Get the temp path
|
||||
size_t charsAvailable = XP_PATH_MAX;
|
||||
int charsAvailable = XP_PATH_MAX;
|
||||
XP_CHAR* p = tempPath;
|
||||
#if (defined(XP_MACOSX) || defined(XP_WIN))
|
||||
if (!childProcessTmpDir || childProcessTmpDir->empty()) {
|
||||
@ -1542,32 +1520,6 @@ ChildFilter(void* context)
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
// Locate the specified executable and store its path as a native string in
|
||||
// the |aPathPtr| so we can later invoke it from within the exception handler.
|
||||
static nsresult
|
||||
LocateExecutable(nsIFile* aXREDirectory, const nsACString& aName,
|
||||
nsAString& aPath)
|
||||
{
|
||||
nsCOMPtr<nsIFile> exePath;
|
||||
nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
exePath->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS"));
|
||||
exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
|
||||
exePath->Append(NS_LITERAL_STRING("Contents"));
|
||||
exePath->Append(NS_LITERAL_STRING("MacOS"));
|
||||
#endif
|
||||
|
||||
exePath->AppendNative(aName);
|
||||
exePath->GetPath(aPath);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif // !defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
nsresult SetExceptionHandler(nsIFile* aXREDirectory,
|
||||
bool force/*=false*/)
|
||||
{
|
||||
@ -1618,33 +1570,30 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory,
|
||||
NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
if (!headlessClient) {
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
// Locate the crash reporter executable
|
||||
nsAutoString crashReporterPath_temp;
|
||||
nsresult rv = LocateExecutable(aXREDirectory,
|
||||
NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME),
|
||||
crashReporterPath_temp);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
// locate crashreporter executable
|
||||
nsCOMPtr<nsIFile> exePath;
|
||||
nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString minidumpAnalyzerPath_temp;
|
||||
rv = LocateExecutable(aXREDirectory,
|
||||
NS_LITERAL_CSTRING(MINIDUMP_ANALYZER_FILENAME),
|
||||
minidumpAnalyzerPath_temp);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
#if defined(XP_MACOSX)
|
||||
exePath->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS"));
|
||||
exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
|
||||
exePath->Append(NS_LITERAL_STRING("Contents"));
|
||||
exePath->Append(NS_LITERAL_STRING("MacOS"));
|
||||
#endif
|
||||
|
||||
exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
|
||||
|
||||
#ifdef XP_WIN32
|
||||
crashReporterPath =
|
||||
reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
|
||||
minidumpAnalyzerPath =
|
||||
reinterpret_cast<wchar_t*>(ToNewUnicode(minidumpAnalyzerPath_temp));
|
||||
#else
|
||||
crashReporterPath = ToNewCString(crashReporterPath_temp);
|
||||
minidumpAnalyzerPath = ToNewCString(minidumpAnalyzerPath_temp);
|
||||
#endif // XP_WIN32
|
||||
nsString crashReporterPath_temp;
|
||||
|
||||
exePath->GetPath(crashReporterPath_temp);
|
||||
crashReporterPath = reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
|
||||
#elif !defined(__ANDROID__)
|
||||
nsCString crashReporterPath_temp;
|
||||
|
||||
exePath->GetNativePath(crashReporterPath_temp);
|
||||
crashReporterPath = ToNewCString(crashReporterPath_temp);
|
||||
#else
|
||||
// On Android, we launch using the application package name instead of a
|
||||
// filename, so use the dynamically set MOZ_ANDROID_PACKAGE_NAME, or fall
|
||||
@ -1658,7 +1607,7 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory,
|
||||
nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
|
||||
crashReporterPath = ToNewCString(package);
|
||||
}
|
||||
#endif // !defined(MOZ_WIDGET_ANDROID)
|
||||
#endif
|
||||
}
|
||||
|
||||
// get temp path to use for minidump path
|
||||
@ -2170,6 +2119,17 @@ static void ReplaceChar(nsCString& str, const nsACString& character,
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
IsInWhitelist(const nsACString& key)
|
||||
{
|
||||
for (size_t i = 0; i < ArrayLength(kCrashEventAnnotations); ++i) {
|
||||
if (key.EqualsASCII(kCrashEventAnnotations[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function is miscompiled with MSVC 2005/2008 when PGO is on.
|
||||
#ifdef _MSC_VER
|
||||
#pragma optimize("", off)
|
||||
@ -2306,7 +2266,9 @@ nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
|
||||
nsAutoCString line = key + kEquals + entry + kNewline;
|
||||
|
||||
crashReporterAPIData->Append(line);
|
||||
crashEventAPIData->Append(line);
|
||||
if (IsInWhitelist(key)) {
|
||||
crashEventAPIData->Append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3068,30 +3030,6 @@ AppendExtraData(const nsAString& id, const AnnotationTable& data)
|
||||
return AppendExtraData(extraFile, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the minidump analyzer program on the specified crash dump. The analyzer
|
||||
* will extract the stack traces from the dump and store them in JSON format as
|
||||
* an annotation in the extra file associated with the crash.
|
||||
*
|
||||
* This method waits synchronously for the program to have finished executing,
|
||||
* do not call it from the main thread!
|
||||
*/
|
||||
void
|
||||
RunMinidumpAnalyzer(const nsAString& id)
|
||||
{
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
nsCOMPtr<nsIFile> file;
|
||||
|
||||
if (CrashReporter::GetMinidumpForID(id, getter_AddRefs(file)) && file) {
|
||||
nsAutoString path;
|
||||
|
||||
file->GetPath(path);
|
||||
LaunchProgram(minidumpAnalyzerPath,
|
||||
(XP_CHAR*)(NS_ConvertUTF16toUTF8(path).get()));
|
||||
}
|
||||
#endif // !defined(MOZ_WIDGET_ANDROID)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers for AppendExtraData()
|
||||
//
|
||||
|
@ -103,7 +103,6 @@ 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);
|
||||
void RunMinidumpAnalyzer(const nsAString& id);
|
||||
|
||||
/*
|
||||
* Renames the stand alone dump file aDumpFile to:
|
||||
|
Loading…
Reference in New Issue
Block a user