mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-03 07:01:19 +00:00
Bug 875562 - Part 8: Write crash events for main process crashes; r=bsmedberg
This patch makes the crash reporter crash event directory aware and writes main process crash event files at crash time. --HG-- extra : rebase_source : 1d6a817ae401230b3fe8fcd236468ceb3e309a10
This commit is contained in:
parent
bdadbd3fbe
commit
a0a4ade497
@ -93,6 +93,7 @@ try {
|
||||
let (crashReporter =
|
||||
Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
|
||||
.getService(Components.interfaces.nsICrashReporter)) {
|
||||
crashReporter.UpdateCrashEventsDir();
|
||||
crashReporter.minidumpPath = do_get_minidumpdir();
|
||||
}
|
||||
}
|
||||
@ -1184,6 +1185,15 @@ function do_get_profile() {
|
||||
let obsSvc = Components.classes["@mozilla.org/observer-service;1"].
|
||||
getService(Components.interfaces.nsIObserverService);
|
||||
|
||||
// We need to update the crash events directory when the profile changes.
|
||||
if (runningInParent &&
|
||||
"@mozilla.org/toolkit/crash-reporter;1" in Components.classes) {
|
||||
let crashReporter =
|
||||
Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
|
||||
.getService(Components.interfaces.nsICrashReporter);
|
||||
crashReporter.UpdateCrashEventsDir();
|
||||
}
|
||||
|
||||
if (!_profileInitialized) {
|
||||
obsSvc.notifyObservers(null, "profile-do-change", "xpcshell-do-get-profile");
|
||||
_profileInitialized = true;
|
||||
|
@ -151,11 +151,16 @@ static const XP_CHAR dumpFileExtension[] = {'.', 'd', 'm', 'p',
|
||||
static const XP_CHAR extraFileExtension[] = {'.', 'e', 'x', 't',
|
||||
'r', 'a', '\0'}; // .extra
|
||||
|
||||
static const char kCrashMainID[] = "crash.main.1\n";
|
||||
|
||||
static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
|
||||
|
||||
static XP_CHAR* pendingDirectory;
|
||||
static XP_CHAR* crashReporterPath;
|
||||
|
||||
// Where crash events should go.
|
||||
static XP_CHAR* eventsDirectory;
|
||||
|
||||
// If this is false, we don't launch the crash reporter
|
||||
static bool doReport = true;
|
||||
|
||||
@ -557,6 +562,63 @@ bool MinidumpCallback(
|
||||
#endif
|
||||
}
|
||||
|
||||
// Write crash event file.
|
||||
|
||||
// Minidump IDs are UUIDs (36) + NULL.
|
||||
static char id_ascii[37];
|
||||
#ifdef XP_LINUX
|
||||
const char * index = strrchr(descriptor.path(), '/');
|
||||
MOZ_ASSERT(index);
|
||||
MOZ_ASSERT(strlen(index) == 1 + 36 + 4); // "/" + UUID + ".dmp"
|
||||
for (uint32_t i = 0; i < 36; i++) {
|
||||
id_ascii[i] = *(index + 1 + i);
|
||||
}
|
||||
#else
|
||||
MOZ_ASSERT(XP_STRLEN(minidump_id) == 36);
|
||||
for (uint32_t i = 0; i < 36; i++) {
|
||||
id_ascii[i] = *((char *)(minidump_id + i));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (eventsDirectory) {
|
||||
static XP_CHAR crashEventPath[XP_PATH_MAX];
|
||||
int size = XP_PATH_MAX;
|
||||
XP_CHAR* p;
|
||||
p = Concat(crashEventPath, eventsDirectory, &size);
|
||||
p = Concat(p, XP_PATH_SEPARATOR, &size);
|
||||
#ifdef XP_LINUX
|
||||
p = Concat(p, id_ascii, &size);
|
||||
#else
|
||||
p = Concat(p, minidump_id, &size);
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN32)
|
||||
HANDLE hFile = CreateFile(crashEventPath, GENERIC_WRITE, 0,
|
||||
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
DWORD nBytes;
|
||||
WriteFile(hFile, kCrashMainID, sizeof(kCrashMainID) - 1, &nBytes,
|
||||
nullptr);
|
||||
WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
|
||||
WriteFile(hFile, "\n", 1, &nBytes, nullptr);
|
||||
WriteFile(hFile, id_ascii, strlen(id_ascii), &nBytes, nullptr);
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
#elif defined(XP_UNIX)
|
||||
int fd = sys_open(crashEventPath,
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
0600);
|
||||
if (fd != -1) {
|
||||
unused << sys_write(fd, kCrashMainID, sizeof(kCrashMainID) - 1);
|
||||
unused << sys_write(fd, crashTimeString, crashTimeStringLen);
|
||||
unused << sys_write(fd, "\n", 1);
|
||||
unused << sys_write(fd, id_ascii, strlen(id_ascii));
|
||||
sys_close(fd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(XP_WIN32)
|
||||
if (!crashReporterAPIData->IsEmpty()) {
|
||||
// write out API data
|
||||
@ -1271,6 +1333,19 @@ InitInstallTime(nsACString& aInstallTime)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Ensure a directory exists and create it if missing.
|
||||
static nsresult
|
||||
EnsureDirectoryExists(nsIFile* dir)
|
||||
{
|
||||
nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Annotate the crash report with a Unique User ID and time
|
||||
// since install. Also do some prep work for recording
|
||||
// time since last crash, which must be calculated at
|
||||
@ -1286,14 +1361,7 @@ nsresult SetupExtraData(nsIFile* aAppDataDirectory,
|
||||
rv = dataDirectory->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool exists;
|
||||
rv = dataDirectory->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!exists) {
|
||||
rv = dataDirectory->Create(nsIFile::DIRECTORY_TYPE, 0700);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
EnsureDirectoryExists(dataDirectory);
|
||||
|
||||
#if defined(XP_WIN32)
|
||||
nsAutoString dataDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_DATA_DIRECTORY="));
|
||||
@ -1439,6 +1507,11 @@ nsresult UnsetExceptionHandler()
|
||||
crashReporterPath = nullptr;
|
||||
}
|
||||
|
||||
if (eventsDirectory) {
|
||||
NS_Free(eventsDirectory);
|
||||
eventsDirectory = nullptr;
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
posix_spawnattr_destroy(&spawnattr);
|
||||
#endif
|
||||
@ -1996,6 +2069,68 @@ nsresult SetSubmitReports(bool aSubmitReports)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
UpdateCrashEventsDir()
|
||||
{
|
||||
nsCOMPtr<nsIFile> eventsDir;
|
||||
|
||||
// We prefer the following locations in order:
|
||||
//
|
||||
// 1. If environment variable is present, use it. We don't expect
|
||||
// the environment variable except for tests and other atypical setups.
|
||||
// 2. Inside the profile directory.
|
||||
// 3. Inside the user application data directory (no profile available).
|
||||
// 4. A temporary directory (setup likely is invalid / application is buggy).
|
||||
const char *env = PR_GetEnv("CRASHES_EVENTS_DIR");
|
||||
if (env) {
|
||||
eventsDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
|
||||
if (!eventsDir) {
|
||||
return;
|
||||
}
|
||||
eventsDir->InitWithNativePath(nsDependentCString(env));
|
||||
EnsureDirectoryExists(eventsDir);
|
||||
} else {
|
||||
nsresult rv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(eventsDir));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
eventsDir->Append(NS_LITERAL_STRING("crashes"));
|
||||
EnsureDirectoryExists(eventsDir);
|
||||
eventsDir->Append(NS_LITERAL_STRING("events"));
|
||||
EnsureDirectoryExists(eventsDir);
|
||||
} else {
|
||||
rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(eventsDir));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
eventsDir->Append(NS_LITERAL_STRING("Crash Reports"));
|
||||
EnsureDirectoryExists(eventsDir);
|
||||
eventsDir->Append(NS_LITERAL_STRING("events"));
|
||||
EnsureDirectoryExists(eventsDir);
|
||||
} else {
|
||||
NS_WARNING("Couldn't get the user appdata directory. Crash events may not be produced.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
nsString path;
|
||||
eventsDir->GetPath(path);
|
||||
eventsDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
|
||||
#else
|
||||
nsCString path;
|
||||
eventsDir->GetNativePath(path);
|
||||
eventsDirectory = ToNewCString(path);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GetCrashEventsDir(nsAString& aPath)
|
||||
{
|
||||
if (!eventsDirectory) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aPath = CONVERT_XP_CHAR_TO_UTF16(eventsDirectory);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
FindPendingDir()
|
||||
{
|
||||
@ -2406,6 +2541,7 @@ OOPInit()
|
||||
dumpMapLock = new Mutex("CrashReporter::dumpMapLock");
|
||||
|
||||
FindPendingDir();
|
||||
UpdateCrashEventsDir();
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -33,6 +33,20 @@ class nsCStringHashKey;
|
||||
namespace CrashReporter {
|
||||
nsresult SetExceptionHandler(nsIFile* aXREDirectory, bool force=false);
|
||||
nsresult UnsetExceptionHandler();
|
||||
|
||||
/**
|
||||
* Tell the crash reporter to recalculate where crash events files should go.
|
||||
*
|
||||
* This should be called during crash reporter initialization and when a
|
||||
* profile is activated or deactivated.
|
||||
*/
|
||||
void UpdateCrashEventsDir();
|
||||
|
||||
/**
|
||||
* Get the path where crash event files should be written.
|
||||
*/
|
||||
bool GetCrashEventsDir(nsAString& aPath);
|
||||
|
||||
bool GetEnabled();
|
||||
bool GetServerURL(nsACString& aServerURL);
|
||||
nsresult SetServerURL(const nsACString& aServerURL);
|
||||
|
@ -12,6 +12,9 @@ let crashReporter =
|
||||
Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
|
||||
.getService(Components.interfaces.nsICrashReporter);
|
||||
|
||||
// We need to call this or crash events go in an undefined location.
|
||||
crashReporter.UpdateCrashEventsDir();
|
||||
|
||||
// Setting the minidump path is not allowed in content processes
|
||||
let processType = Components.classes["@mozilla.org/xre/runtime;1"].
|
||||
getService(Components.interfaces.nsIXULRuntime).processType;
|
||||
|
@ -1,3 +1,9 @@
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
function getEventDir() {
|
||||
return OS.Path.join(do_get_tempdir().path, "crash-events");
|
||||
}
|
||||
|
||||
/*
|
||||
* Run an xpcshell subprocess and crash it.
|
||||
*
|
||||
@ -55,10 +61,25 @@ function do_crash(setup, callback, canReturnZero)
|
||||
args.push('-e', setup);
|
||||
}
|
||||
args.push('-f', tailfile.path);
|
||||
|
||||
let env = Components.classes["@mozilla.org/process/environment;1"]
|
||||
.getService(Components.interfaces.nsIEnvironment);
|
||||
|
||||
let crashD = do_get_tempdir();
|
||||
crashD.append("crash-events");
|
||||
if (!crashD.exists()) {
|
||||
crashD.create(crashD.DIRECTORY_TYPE, 0700);
|
||||
}
|
||||
|
||||
env.set("CRASHES_EVENTS_DIR", crashD.path);
|
||||
|
||||
try {
|
||||
process.run(true, args, args.length);
|
||||
}
|
||||
catch(ex) {} // on Windows we exit with a -1 status when crashing.
|
||||
finally {
|
||||
env.set("CRASHES_EVENTS_DIR", "");
|
||||
}
|
||||
|
||||
if (!canReturnZero) {
|
||||
// should exit with an error (should have crashed)
|
||||
|
42
toolkit/crashreporter/test/unit/test_event_files.js
Normal file
42
toolkit/crashreporter/test/unit/test_event_files.js
Normal file
@ -0,0 +1,42 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://testing-common/AppData.jsm", this);
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_setup() {
|
||||
do_get_profile();
|
||||
yield makeFakeAppDir();
|
||||
});
|
||||
|
||||
add_task(function* test_main_process_crash() {
|
||||
let cm = Services.crashmanager;
|
||||
Assert.ok(cm, "CrashManager available.");
|
||||
|
||||
let basename;
|
||||
let deferred = Promise.defer();
|
||||
do_crash("crashType = CrashTestUtils.CRASH_RUNTIMEABORT;",
|
||||
(minidump, extra) => {
|
||||
basename = minidump.leafName;
|
||||
cm._eventsDirs = [getEventDir()];
|
||||
cm.aggregateEventsFiles().then(deferred.resolve, deferred.reject);
|
||||
},
|
||||
true);
|
||||
|
||||
let count = yield deferred.promise;
|
||||
Assert.equal(count, 1, "A single crash event file was seen.");
|
||||
let crashes = yield cm.getCrashes();
|
||||
Assert.equal(crashes.length, 1);
|
||||
let crash = crashes[0];
|
||||
Assert.ok(crash.isMainProcessCrash);
|
||||
Assert.equal(crash.id + ".dmp", basename, "ID recorded properly");
|
||||
});
|
@ -26,3 +26,4 @@ run-if = os == 'win' || os == 'linux'
|
||||
skip-if = os=='linux' && bits==32
|
||||
|
||||
[test_crash_AsyncShutdown.js]
|
||||
[test_event_files.js]
|
||||
|
@ -1110,6 +1110,13 @@ nsXULAppInfo::SetSubmitReports(bool aEnabled)
|
||||
return CrashReporter::SetSubmitReports(aEnabled);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULAppInfo::UpdateCrashEventsDir()
|
||||
{
|
||||
CrashReporter::UpdateCrashEventsDir();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static const nsXULAppInfo kAppInfo;
|
||||
@ -2991,6 +2998,7 @@ XREMain::XRE_mainInit(bool* aExitFlag)
|
||||
if ((mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) &&
|
||||
NS_SUCCEEDED(
|
||||
CrashReporter::SetExceptionHandler(mAppData->xreDirectory))) {
|
||||
CrashReporter::UpdateCrashEventsDir();
|
||||
if (mAppData->crashReporterURL)
|
||||
CrashReporter::SetServerURL(nsDependentCString(mAppData->crashReporterURL));
|
||||
|
||||
@ -3644,6 +3652,8 @@ XREMain::XRE_mainStartup(bool* aExitFlag)
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
|
||||
MakeOrSetMinidumpPath(mProfD);
|
||||
|
||||
CrashReporter::UpdateCrashEventsDir();
|
||||
#endif
|
||||
|
||||
nsAutoCString version;
|
||||
|
@ -109,4 +109,12 @@ interface nsICrashReporter : nsISupports
|
||||
* User preference for submitting crash reports.
|
||||
*/
|
||||
attribute boolean submitReports;
|
||||
|
||||
/**
|
||||
* Cause the crash reporter to re-evaluate where crash events should go.
|
||||
*
|
||||
* This should be called during application startup and whenever profiles
|
||||
* change.
|
||||
*/
|
||||
void UpdateCrashEventsDir();
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user