mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 888373: Simple API for detecting startup/shutdown crashes;r=yoric,gps
This commit is contained in:
parent
f87982eddb
commit
8e01e3a394
@ -379,6 +379,8 @@
|
||||
@BINPATH@/components/Downloads.manifest
|
||||
@BINPATH@/components/DownloadLegacy.js
|
||||
@BINPATH@/components/BrowserPageThumbs.manifest
|
||||
@BINPATH@/components/crashmonitor.manifest
|
||||
@BINPATH@/components/nsCrashMonitor.js
|
||||
@BINPATH@/components/SiteSpecificUserAgent.js
|
||||
@BINPATH@/components/SiteSpecificUserAgent.manifest
|
||||
@BINPATH@/components/toolkitsearch.manifest
|
||||
|
208
toolkit/components/crashmonitor/CrashMonitor.jsm
Normal file
208
toolkit/components/crashmonitor/CrashMonitor.jsm
Normal file
@ -0,0 +1,208 @@
|
||||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Crash Monitor
|
||||
*
|
||||
* Monitors execution of a program to detect possible crashes. After
|
||||
* program termination, the monitor can be queried during the next run
|
||||
* to determine whether the last run exited cleanly or not.
|
||||
*
|
||||
* The monitoring is done by registering and listening for special
|
||||
* notifications, or checkpoints, known to be sent by the monitored
|
||||
* program as different stages in the execution are reached. As they
|
||||
* are observed, these notifications are written asynchronously to a
|
||||
* checkpoint file.
|
||||
*
|
||||
* During next program startup the crash monitor reads the checkpoint
|
||||
* file from the last session. If notifications are missing, a crash
|
||||
* has likely happened. By inspecting the notifications present, it is
|
||||
* possible to determine what stages were reached in the program
|
||||
* before the crash.
|
||||
*
|
||||
* Note that since the file is written asynchronously it is possible
|
||||
* that a received notification is lost if the program crashes right
|
||||
* after a checkpoint, but before crash monitor has been able to write
|
||||
* it to disk. Thus, while the presence of a notification in the
|
||||
* checkpoint file tells us that the corresponding stage was reached
|
||||
* during the last run, the absence of a notification after a crash
|
||||
* does not necessarily tell us that the checkpoint wasn't reached.
|
||||
*/
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "CrashMonitor" ];
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
|
||||
|
||||
const NOTIFICATIONS = [
|
||||
"final-ui-startup",
|
||||
"sessionstore-windows-restored",
|
||||
"quit-application-granted",
|
||||
"quit-application",
|
||||
"profile-change-net-teardown",
|
||||
"profile-change-teardown",
|
||||
"profile-before-change",
|
||||
"sessionstore-final-state-write-complete"
|
||||
];
|
||||
|
||||
let CrashMonitorInternal = {
|
||||
|
||||
/**
|
||||
* Notifications received during the current session.
|
||||
*
|
||||
* Object where a property with a value of |true| means that the
|
||||
* notification of the same name has been received at least once by
|
||||
* the CrashMonitor during this session. Notifications that have not
|
||||
* yet been received are not present as properties. |NOTIFICATIONS|
|
||||
* lists the notifications tracked by the CrashMonitor.
|
||||
*/
|
||||
checkpoints: {},
|
||||
|
||||
/**
|
||||
* Notifications received during previous session.
|
||||
*
|
||||
* Available after |loadPreviousCheckpoints|. Promise which resolves
|
||||
* to an object containing a set of properties, where a property
|
||||
* with a value of |true| means that the notification with the same
|
||||
* name as the property name was received at least once last
|
||||
* session.
|
||||
*/
|
||||
previousCheckpoints: null,
|
||||
|
||||
/* Deferred for AsyncShutdown blocker */
|
||||
profileBeforeChangeDeferred: Promise.defer(),
|
||||
|
||||
/**
|
||||
* Path to checkpoint file.
|
||||
*
|
||||
* Each time a new notification is received, this file is written to
|
||||
* disc to reflect the information in |checkpoints|.
|
||||
*/
|
||||
path: OS.Path.join(OS.Constants.Path.profileDir, "sessionCheckpoints.json"),
|
||||
|
||||
/**
|
||||
* Load checkpoints from previous session asynchronously.
|
||||
*
|
||||
* @return {Promise} A promise that resolves/rejects once loading is complete
|
||||
*/
|
||||
loadPreviousCheckpoints: function () {
|
||||
let promise = Task.spawn(function () {
|
||||
let notifications;
|
||||
try {
|
||||
let decoder = new TextDecoder();
|
||||
let data = yield OS.File.read(CrashMonitorInternal.path);
|
||||
let contents = decoder.decode(data);
|
||||
notifications = JSON.parse(contents);
|
||||
} catch (ex if ex instanceof OS.File.Error && ex.becauseNoSuchFile) {
|
||||
// If checkpoint file cannot be read
|
||||
throw new Task.Result(null);
|
||||
} catch (ex) {
|
||||
Cu.reportError("Error while loading crash monitor data: " + ex);
|
||||
throw new Task.Result(null);
|
||||
}
|
||||
throw new Task.Result(Object.freeze(notifications));
|
||||
});
|
||||
|
||||
CrashMonitorInternal.previousCheckpoints = promise;
|
||||
return promise;
|
||||
}
|
||||
};
|
||||
|
||||
this.CrashMonitor = {
|
||||
|
||||
/**
|
||||
* Notifications received during previous session.
|
||||
*
|
||||
* Return object containing the set of notifications received last
|
||||
* session as keys with values set to |true|.
|
||||
*
|
||||
* @return {Promise} A promise resolving to previous checkpoints
|
||||
*/
|
||||
get previousCheckpoints() {
|
||||
if (!CrashMonitorInternal.initialized) {
|
||||
throw new Error("CrashMonitor must be initialized before getting previous checkpoints");
|
||||
}
|
||||
|
||||
return CrashMonitorInternal.previousCheckpoints
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize CrashMonitor.
|
||||
*
|
||||
* Should only be called from the CrashMonitor XPCOM component.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
init: function () {
|
||||
if (CrashMonitorInternal.initialized) {
|
||||
throw new Error("CrashMonitor.init() must only be called once!");
|
||||
}
|
||||
|
||||
let promise = CrashMonitorInternal.loadPreviousCheckpoints();
|
||||
// Add "profile-after-change" to checkpoint as this method is
|
||||
// called after receiving it
|
||||
CrashMonitorInternal.checkpoints["profile-after-change"] = true;
|
||||
|
||||
NOTIFICATIONS.forEach(function (aTopic) {
|
||||
Services.obs.addObserver(this, aTopic, false);
|
||||
}, this);
|
||||
|
||||
// Add shutdown blocker for profile-before-change
|
||||
AsyncShutdown.profileBeforeChange.addBlocker(
|
||||
"CrashMonitor: Writing notifications to file after receiving profile-before-change",
|
||||
CrashMonitorInternal.profileBeforeChangeDeferred.promise
|
||||
);
|
||||
|
||||
CrashMonitorInternal.initialized = true;
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle registered notifications.
|
||||
*
|
||||
* Update checkpoint file for every new notification received.
|
||||
*/
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
if (!(aTopic in CrashMonitorInternal.checkpoints)) {
|
||||
// If this is the first time this notification is received,
|
||||
// remember it and write it to file
|
||||
CrashMonitorInternal.checkpoints[aTopic] = true;
|
||||
Task.spawn(function() {
|
||||
try {
|
||||
let data = JSON.stringify(CrashMonitorInternal.checkpoints);
|
||||
|
||||
/* Write to the checkpoint file asynchronously, off the main
|
||||
* thread, for performance reasons. Note that this means
|
||||
* that there's not a 100% guarantee that the file will be
|
||||
* written by the time the notification completes. The
|
||||
* exception is profile-before-change which has a shutdown
|
||||
* blocker. */
|
||||
yield OS.File.writeAtomic(
|
||||
CrashMonitorInternal.path,
|
||||
data, {tmpPath: CrashMonitorInternal.path + ".tmp"});
|
||||
|
||||
} finally {
|
||||
// Resolve promise for blocker
|
||||
if (aTopic == "profile-before-change") {
|
||||
CrashMonitorInternal.profileBeforeChangeDeferred.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (NOTIFICATIONS.every(elem => elem in CrashMonitorInternal.checkpoints)) {
|
||||
// All notifications received, unregister observers
|
||||
NOTIFICATIONS.forEach(function (aTopic) {
|
||||
Services.obs.removeObserver(this, aTopic);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
};
|
||||
Object.freeze(this.CrashMonitor);
|
3
toolkit/components/crashmonitor/crashmonitor.manifest
Normal file
3
toolkit/components/crashmonitor/crashmonitor.manifest
Normal file
@ -0,0 +1,3 @@
|
||||
component {d9d75e86-8f17-4c57-993e-f738f0d86d42} nsCrashMonitor.js
|
||||
contract @mozilla.org/toolkit/crashmonitor;1 {d9d75e86-8f17-4c57-993e-f738f0d86d42}
|
||||
category profile-after-change CrashMonitor @mozilla.org/toolkit/crashmonitor;1
|
17
toolkit/components/crashmonitor/moz.build
Normal file
17
toolkit/components/crashmonitor/moz.build
Normal file
@ -0,0 +1,17 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_JS_MODULES = [
|
||||
'CrashMonitor.jsm',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'crashmonitor.manifest',
|
||||
]
|
||||
|
||||
EXTRA_PP_COMPONENTS += [
|
||||
'nsCrashMonitor.js',
|
||||
]
|
29
toolkit/components/crashmonitor/nsCrashMonitor.js
Normal file
29
toolkit/components/crashmonitor/nsCrashMonitor.js
Normal file
@ -0,0 +1,29 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let Scope = {}
|
||||
Components.utils.import("resource://gre/modules/CrashMonitor.jsm", Scope);
|
||||
let MonitorAPI = Scope.CrashMonitor;
|
||||
|
||||
function CrashMonitor() {};
|
||||
|
||||
CrashMonitor.prototype = {
|
||||
|
||||
classID: Components.ID("{d9d75e86-8f17-4c57-993e-f738f0d86d42}"),
|
||||
contractID: "@mozilla.org/toolkit/crashmonitor;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIObserver]),
|
||||
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "profile-after-change":
|
||||
MonitorAPI.init();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([CrashMonitor]);
|
@ -16,6 +16,7 @@ PARALLEL_DIRS += [
|
||||
'console',
|
||||
'contentprefs',
|
||||
'cookie',
|
||||
'crashmonitor',
|
||||
'diskspacewatcher',
|
||||
'downloads',
|
||||
'exthelper',
|
||||
|
Loading…
Reference in New Issue
Block a user