Bug 836186 - Don't load FHR providers until they are used; r=rnewman

This commit is contained in:
Gregory Szorc 2013-01-31 08:58:19 -08:00
parent 69ace41ee9
commit 5d1199412e
8 changed files with 137 additions and 5 deletions

View File

@ -129,6 +129,8 @@ function HealthReporter(branch, policy, sessionRecorder) {
this._shutdownComplete = false;
this._shutdownCompleteCallback = null;
this._constantOnlyProviders = {};
this._ensureDirectoryExists(this._stateDir)
.then(this._onStateDirCreated.bind(this),
this._onInitError.bind(this));
@ -549,10 +551,15 @@ HealthReporter.prototype = Object.freeze({
let ns = {};
Cu.import(uri, ns);
let provider = new ns[entry]();
provider.initPreferences(this._branch + "provider.");
provider.healthReporter = this;
promises.push(this.registerProvider(provider));
let proto = ns[entry].prototype;
if (proto.constantOnly) {
this._log.info("Provider is constant-only. Deferring initialization: " +
proto.name);
this._constantOnlyProviders[proto.name] = ns[entry];
} else {
let provider = this.initProviderFromType(ns[entry]);
promises.push(this.registerProvider(provider));
}
} catch (ex) {
this._log.warn("Error registering provider from category manager: " +
entry + "; " + CommonUtils.exceptionStr(ex));
@ -567,11 +574,54 @@ HealthReporter.prototype = Object.freeze({
});
},
initProviderFromType: function (providerType) {
let provider = new providerType();
provider.initPreferences(this._branch + "provider.");
provider.healthReporter = this;
return provider;
},
/**
* Collect all measurements for all registered providers.
*/
collectMeasurements: function () {
return this._collector.collectConstantData();
return Task.spawn(function doCollection() {
for each (let providerType in this._constantOnlyProviders) {
try {
let provider = this.initProviderFromType(providerType);
yield this.registerProvider(provider);
} catch (ex) {
this._log.warn("Error registering constant-only provider: " +
CommonUtils.exceptionStr(ex));
}
}
let result;
try {
result = yield this._collector.collectConstantData();
} finally {
for (let provider of this._collector.providers) {
if (!provider.constantOnly) {
continue;
}
this._log.info("Shutting down constant-only provider: " +
provider.name);
try {
yield provider.shutdown();
} catch (ex) {
this._log.warn("Error when shutting down provider: " +
CommonUtils.exceptionStr(ex));
} finally {
this._collector.unregisterProvider(provider.name);
}
}
}
throw new Task.Result(result);
}.bind(this));
},
/**

View File

@ -214,6 +214,8 @@ ProfileMetadataProvider.prototype = {
measurementTypes: [ProfileMetadataMeasurement],
constantOnly: true,
getProfileCreationDays: function () {
let accessor = new ProfileCreationTimeAccessor(null, this._log);

View File

@ -121,6 +121,8 @@ AppInfoProvider.prototype = Object.freeze({
measurementTypes: [AppInfoMeasurement, AppVersionMeasurement],
constantOnly: true,
appInfoFields: {
// From nsIXULAppInfo.
vendor: "vendor",
@ -296,6 +298,8 @@ SysInfoProvider.prototype = Object.freeze({
measurementTypes: [SysInfoMeasurement],
constantOnly: true,
sysInfoFields: {
cpucount: "cpuCount",
memsize: "memoryMB",
@ -477,6 +481,8 @@ SessionsProvider.prototype = Object.freeze({
measurementTypes: [CurrentSessionMeasurement, PreviousSessionsMeasurement],
constantOnly: true,
collectConstantData: function () {
let previous = this.getMeasurement("previous", 2);
@ -746,6 +752,8 @@ CrashesProvider.prototype = Object.freeze({
measurementTypes: [DailyCrashesMeasurement],
constantOnly: true,
collectConstantData: function () {
return Task.spawn(this._populateCrashCounts.bind(this));
},

View File

@ -171,6 +171,39 @@ add_task(function test_register_providers_from_category_manager() {
reporter._shutdown();
});
// Constant only providers are only initialized at constant collect
// time.
add_task(function test_constant_only_providers() {
const category = "healthreporter-constant-only";
let cm = Cc["@mozilla.org/categorymanager;1"]
.getService(Ci.nsICategoryManager);
cm.addCategoryEntry(category, "DummyProvider",
"resource://testing-common/services/metrics/mocks.jsm",
false, true);
cm.addCategoryEntry(category, "DummyConstantProvider",
"resource://testing-common/services/metrics/mocks.jsm",
false, true);
let reporter = yield getReporter("constant_only_providers");
do_check_eq(reporter._collector._providers.size, 0);
yield reporter.registerProvidersFromCategoryManager(category);
do_check_eq(reporter._collector._providers.size, 1);
do_check_true(reporter._storage.hasProvider("DummyProvider"));
do_check_false(reporter._storage.hasProvider("DummyConstantProvider"));
yield reporter.collectMeasurements();
do_check_eq(reporter._collector._providers.size, 1);
do_check_true(reporter._storage.hasProvider("DummyConstantProvider"));
let mID = reporter._storage.measurementID("DummyConstantProvider", "DummyMeasurement", 1);
let values = yield reporter._storage.getMeasurementValues(mID);
do_check_true(values.singular.size > 0);
reporter._shutdown();
});
add_task(function test_json_payload_simple() {
let reporter = yield getReporter("json_payload_simple");

View File

@ -81,6 +81,16 @@ Collector.prototype = Object.freeze({
return deferred.promise;
},
/**
* Remove a named provider from the collector.
*
* It is the caller's responsibility to shut down the provider
* instance.
*/
unregisterProvider: function (name) {
this._providers.delete(name);
},
_popAndInitProvider: function () {
if (!this._providerInitQueue.length || this._providerInitializing) {
return;

View File

@ -391,6 +391,20 @@ this.Provider = function () {
}
Provider.prototype = Object.freeze({
/**
* Whether the provider provides only constant data.
*
* If this is true, the provider likely isn't instantiated until
* `collectConstantData` is called and the provider may be torn down after
* this function has finished.
*
* This is an optimization so provider instances aren't dead weight while the
* application is running.
*
* This must be set on the prototype for the optimization to be realized.
*/
constantOnly: false,
/**
* Obtain a `Measurement` from its name and version.
*

View File

@ -7,6 +7,7 @@
this.EXPORTED_SYMBOLS = [
"DummyMeasurement",
"DummyProvider",
"DummyConstantProvider",
];
const {utils: Cu} = Components;
@ -87,3 +88,14 @@ DummyProvider.prototype = {
};
this.DummyConstantProvider = function () {
DummyProvider.call(this, "DummyConstantProvider");
}
DummyConstantProvider.prototype = {
__proto__: DummyProvider.prototype,
constantOnly: true,
};

View File

@ -42,6 +42,9 @@ add_task(function test_register_provider() {
failed = false;
}
collector.unregisterProvider(dummy.name);
do_check_eq(collector._providers.size, 0);
yield storage.close();
});