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:
Phil Ringnalda 2016-12-05 20:34:55 -08:00
parent b36c06e1aa
commit c8e1b94ffa
9 changed files with 238 additions and 613 deletions

View File

@ -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, &notes);
}
/**
* 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

View File

@ -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;

View File

@ -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":

View File

@ -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) {

View File

@ -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
===============

View File

@ -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();

View File

@ -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``.

View File

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

View File

@ -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: