Closed Bug 1360261 Opened 8 years ago Closed 7 years ago

GMPInstallManager causes jank off a timer in _delayedStartup

Categories

(Core Graveyard :: Plug-ins, defect, P1)

defect

Tracking

(Performance Impact:medium, firefox56 fixed)

RESOLVED FIXED
mozilla56
Performance Impact medium
Tracking Status
firefox56 --- fixed

People

(Reporter: florian, Assigned: daleharvey)

References

(Blocks 2 open bugs)

Details

(Whiteboard: [reserve-photon-performance])

Attachments

(1 file, 1 obsolete file)

See this profile where GMPInstallManager causes 15ms of jank off a timer started in _delayedStartup: https://perfht.ml/2qjgDbE Here are few things that could be done to reduce this: - use requestIdleCallback instead of the timer at http://searchfox.org/mozilla-central/rev/ce5ccb6a8ca803271c946ccb8b43b7e7d8b64e7a/browser/base/content/browser.js#1547 - avoid using Preferences.jsm in GMPUtils.jsm - avoid using NetUtil in GMPUtils.jsm. Is there a reason why getting the locale uses the complicated code at http://searchfox.org/mozilla-central/rev/ce5ccb6a8ca803271c946ccb8b43b7e7d8b64e7a/toolkit/modules/UpdateUtils.jsm#119 instead of Services.locale.getRequestedLocale()? - replace UpdateUtils.jsm's formatUpdateURL method with Services.urlFormatter, or if that's not possible for some reason, reimplement it in a more efficient way. Eg. a single .replace call with a callback function that gets the values of the parameters only if they are actually part of the URL (see bug 1356593 for an example of what I mean). Note: GMPInstallManager also causes jank later by doing main thread I/O while installing updates; that's covered by bug 1149732.
Flags: qe-verify?
Priority: -- → P2
I think moving this over to requestIdleCallback is definitely a great first step which should be done independently to making GMPInstallManager itself more efficient. There seems to be no good reason why this needs to run in exactly 1 minute.
Depends on: 1360864
(In reply to :Ehsan Akhgari (super long backlog, slow to respond) from comment #1) > I think moving this over to requestIdleCallback is definitely a great first > step which should be done independently to making GMPInstallManager itself > more efficient. There seems to be no good reason why this needs to run in > exactly 1 minute. Posted a patch in bug 1360864 for this.
(In reply to Florian Quèze [:florian] [:flo] from comment #0) > See this profile where GMPInstallManager causes 15ms of jank off a timer > started in _delayedStartup: https://perfht.ml/2qjgDbE > > Here are few things that could be done to reduce this: - initialization should move to nsBrowserGlue since the module doesn't seem to care about individual windows
Whiteboard: [photon-performance][qf] → [photon-performance][qf:p1]
moving over to where code for plugins lives so the right folks can triage
Component: Add-ons Manager → Plug-ins
Product: Toolkit → Core
No longer blocks: photon-performance-triage
Priority: P2 → P3
Whiteboard: [photon-performance][qf:p1] → [reserve-photon-performance][qf:p1]
Whiteboard: [reserve-photon-performance][qf:p1] → [reserve-photon-performance][qf:p2]
No longer blocks: qf-bugs-upforgrabs
Flags: qe-verify? → qe-verify-
Assignee: nobody → dale
Status: NEW → ASSIGNED
Priority: P3 → P1
Attached patch remove-preference.patch (obsolete) (deleted) — Splinter Review
The charPref usage in Services.prefs does not like null / undefined so replaced usage of that with "", I started switching all usage of GMPPrefs to use Services.prefs directly but this ended up a lot clearer a change even if the extra functions in GMPPrefs are a little ott I havent addressed the other issues, will do seperate patches
Attachment #8882000 - Flags: review?(florian)
Comment on attachment 8882000 [details] [diff] [review] remove-preference.patch Review of attachment 8882000 [details] [diff] [review]: ----------------------------------------------------------------- ::: toolkit/modules/GMPInstallManager.jsm @@ +199,1 @@ > DEFAULT_SECONDS_BETWEEN_CHECKS) nit: fix indent ::: toolkit/modules/GMPUtils.jsm @@ +144,5 @@ > * @param aDefaultValue The default value if no preference exists. > * @param aPlugin The plugin to scope the preference to. > * @return The obtained preference value, or the defaultValue if none exists. > */ > get(aKey, aDefaultValue, aPlugin) { This should be called "getString", keeping the generic "get" name is confusing when this method handles only one pref type. @@ +149,5 @@ > if (aKey === this.KEY_APP_DISTRIBUTION || > aKey === this.KEY_APP_DISTRIBUTION_VERSION) { > return Services.prefs.getDefaultBranch(null).getCharPref(aKey, "default"); > } > + return Services.prefs.getCharPref(this.getPrefKey(aKey, aPlugin), aDefaultValue); Preferences.get does not use .getCharPref, under the hood it's .getComplexValue(prefName, Ci.nsISupportsString).data, the modern equivalent of which is .getStringPref @@ +180,5 @@ > * @param aKey The preference key value to use. > * @param aVal The value to set. > * @param aPlugin The plugin to scope the preference to. > */ > set(aKey, aVal, aPlugin) { This should be called setString @@ +181,5 @@ > * @param aVal The value to set. > * @param aPlugin The plugin to scope the preference to. > */ > set(aKey, aVal, aPlugin) { > + Services.prefs.setCharPref(this.getPrefKey(aKey, aPlugin), aVal); use setStringPref instead of setCharPref ::: toolkit/mozapps/extensions/internal/GMPProvider.jsm @@ +175,3 @@ > }, > set userDisabled(aVal) { > + GMPPrefs.setBool(GMPPrefs.KEY_PLUGIN_ENABLED, The indent was already oddly broken here, but let's fix it while we are touching this call. @@ +196,5 @@ > return permissions; > }, > > get updateDate() { > + let time = Number(GMPPrefs.getInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, null, Did you mean 0 instead of null? @@ +201,1 @@ > this._plugin.id)); indent @@ +201,2 @@ > this._plugin.id)); > if (!isNaN(time) && this.isInstalled) { Maybe this check needs to be adjusted if 'time' will now be 0 instead of null when the pref doesn't exist.
Attachment #8882000 - Flags: review?(florian) → feedback+
Comment on attachment 8883995 [details] Bug 1360261 - Remove Preferences.jsm usage in GMPUtils. https://reviewboard.mozilla.org/r/154952/#review160258 ::: toolkit/modules/GMPInstallManager.jsm:70 (Diff revision 1) > // the normal URL but it does not check the cert. > - let url = GMPPrefs.get(GMPPrefs.KEY_URL_OVERRIDE); > + let url = GMPPrefs.getString(GMPPrefs.KEY_URL_OVERRIDE, ""); > if (url) { > log.info("Using override url: " + url); > } else { > - url = GMPPrefs.get(GMPPrefs.KEY_URL); > + url = GMPPrefs.getString(GMPPrefs.KEY_URL); Do you need a default value here, or is this pref always guaranteed to exist? http://searchfox.org/mozilla-central/rev/f1472e5b57fea8d4a7bbdd81c44e46958fe3c1ce/modules/libpref/init/all.js#5477 looks like it will always exist so that's probably fine. ::: toolkit/modules/GMPInstallManager.jsm:103 (Diff revision 1) > let url = this._getURL(); > > let allowNonBuiltIn = true; > let certs = null; > if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE)) { > - allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_REQUIREBUILTIN, true); > + allowNonBuiltIn = !GMPPrefs.getString(GMPPrefs.KEY_CERT_REQUIREBUILTIN, true); The default value of getStringPref will always be a string, so true will be automatically converted to "true" here, which should be fine. ::: toolkit/modules/GMPUtils.jsm:185 (Diff revision 1) > + * @param aKey The preference key value to use. > + * @param aVal The value to set. > + * @param aPlugin The plugin to scope the preference to. > + */ > + setString(aKey, aVal, aPlugin) { > + Services.prefs.setCharPref(this.getPrefKey(aKey, aPlugin), aVal); setStringPref ::: toolkit/modules/UpdateUtils.jsm:66 (Diff revision 1) > * @param url > * The URL to format. > * @return The formatted URL. > */ > formatUpdateURL(url) { > - url = url.replace(/%PRODUCT%/g, Services.appinfo.name); > + const PARAM_REGEXP = /\%((?:\w+:)?\w+)(\??)\%/g; This can be simplified a lot, you don't need to support prefixes or optional parameters. I think you just need: /%(\w+)%/g And at this point it's probably simple enough that you can just inline it inside the .replace call. ::: toolkit/modules/UpdateUtils.jsm:67 (Diff revision 1) > * The URL to format. > * @return The formatted URL. > */ > formatUpdateURL(url) { > - url = url.replace(/%PRODUCT%/g, Services.appinfo.name); > - url = url.replace(/%VERSION%/g, Services.appinfo.version); > + const PARAM_REGEXP = /\%((?:\w+:)?\w+)(\??)\%/g; > + return url.replace(PARAM_REGEXP, (match, name, optional) => { You don't need the 'optional' parameter. ::: toolkit/modules/UpdateUtils.jsm:68 (Diff revision 1) > * @return The formatted URL. > */ > formatUpdateURL(url) { > - url = url.replace(/%PRODUCT%/g, Services.appinfo.name); > - url = url.replace(/%VERSION%/g, Services.appinfo.version); > - url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID); > + const PARAM_REGEXP = /\%((?:\w+:)?\w+)(\??)\%/g; > + return url.replace(PARAM_REGEXP, (match, name, optional) => { > + if (name === "PRODUCT") return Services.appinfo.name; nit: Our js code usually uses '==', unless '===' is specifically needed. This may be more readable as a switch/case. ::: toolkit/modules/UpdateUtils.jsm:79 (Diff revision 1) > - } > - url = url.replace(/%CHANNEL%/g, this.UpdateChannel); > - url = url.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion); > - url = url.replace(/%DISTRIBUTION%/g, > - getDistributionPrefValue(PREF_APP_DISTRIBUTION)); > - url = url.replace(/%DISTRIBUTION_VERSION%/g, > + if (name === "CHANNEL") return this.UpdateChannel; > + if (name === "PLATFORM_VERSION") return Services.appinfo.platformVersion; > + if (name === "SYSTEM_CAPABILITIES") > + return getSystemCapabilities(); > + if (name === "CUSTOM") > + return Preferences.get(PREF_APP_UPDATE_CUSTOM, ""); This is the only use of Preferences.jsm in this file, can we remove it? :-) ::: toolkit/modules/UpdateUtils.jsm:83 (Diff revision 1) > - getDistributionPrefValue(PREF_APP_DISTRIBUTION)); > - url = url.replace(/%DISTRIBUTION_VERSION%/g, > - getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION)); > - url = url.replace(/%CUSTOM%/g, Preferences.get(PREF_APP_UPDATE_CUSTOM, "")); > - url = url.replace(/\+/g, "%2B"); > - > + if (name === "CUSTOM") > + return Preferences.get(PREF_APP_UPDATE_CUSTOM, ""); > + if (name === "DISTRIBUTION") > + return getDistributionPrefValue(PREF_APP_DISTRIBUTION); > + if (name === "DISTRIBUTION_VERSION") > + return getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION); Isn't this function going to cause a JS strict warning because it doesn't always return a value? I think it should 'return match;' at the end. ::: toolkit/mozapps/extensions/internal/GMPProvider.jsm:140 (Diff revision 1) > get gmpPath() { > if (!this._gmpPath && this.isInstalled) { > this._gmpPath = OS.Path.join(OS.Constants.Path.profileDir, > this._plugin.id, > - GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION, > + GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, > null, this._plugin.id)); please fix indent here ::: toolkit/mozapps/extensions/internal/GMPProvider.jsm:156 (Diff revision 1) > > get description() { return this._plugin.description; }, > get fullDescription() { return this._plugin.fullDescription; }, > > get version() { > - return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION, null, > + return GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, null, this._plugin.id); I'm pleasantly surprised that getStringPref accepts null as a default value; note: getCharPref throws if null is provided as the default value. Maybe I should fix that to make these 2 consistent. ::: toolkit/mozapps/extensions/internal/GMPProvider.jsm:199 (Diff revision 1) > }, > > get updateDate() { > - let time = Number(GMPPrefs.get(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, null, > + let time = Number(GMPPrefs.getInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, 0, > - this._plugin.id)); > + this._plugin.id)); > if (!isNaN(time) && this.isInstalled) { time will always be a number now, should '!isNaN(time)' be changed to 'time' ?
Attachment #8883995 - Flags: review?(florian) → review-
Comment on attachment 8883995 [details] Bug 1360261 - Remove Preferences.jsm usage in GMPUtils. https://reviewboard.mozilla.org/r/154952/#review161282 ::: toolkit/mozapps/extensions/internal/GMPProvider.jsm:140 (Diff revision 2) > get gmpPath() { > if (!this._gmpPath && this.isInstalled) { > this._gmpPath = OS.Path.join(OS.Constants.Path.profileDir, > this._plugin.id, > - GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION, > + GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, > null, this._plugin.id)); The indent still needs fixing here.
Attachment #8883995 - Flags: review?(florian) → review+
Fixed the indent, also eslint didnt enjoy the spaced out switch statement so just put them on newlines, try @ https://treeherder.mozilla.org/#/jobs?repo=try&revision=97095052fd1ebcb28729360184c47d7b35dbaba9 Cheers
Pushed by dharvey@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/eab68649cc1b Remove Preferences.jsm usage in GMPUtils. r=florian
(In reply to Florian Quèze [:florian] [:flo] from comment #0) > - avoid using NetUtil in GMPUtils.jsm. Is there a reason why getting the > locale uses the complicated code at > http://searchfox.org/mozilla-central/rev/ > ce5ccb6a8ca803271c946ccb8b43b7e7d8b64e7a/toolkit/modules/UpdateUtils.jsm#119 > instead of Services.locale.getRequestedLocale()? There may be. `getRequestedLocales` may be changed by users by hand and soon from Preferences window. Also, the requested is not necessary the app's locale - user may request ['de', 'fr', 'en-US'], and if we only have locales for 'fr' and 'en-US', then their Firefox will be in 'fr'. There's also `getAppLocales` which shows only negotiated locales (so, ['fr', 'en-US'] in the above case), but this one can also change if the user downloads a 'de' language pack and installs it. Now, if our update mechanism will handle downloading an update for a different locale than the original app was installed for, then I believe that we should use `getAppLocales`. In that scenario the order would be: 1) User downloads Firefox in language A 2) User downloads language pack in language B 3) User switches his locale to B 4) We download an update for Firefox in locale B 5) Profit... But if that doesn't work, and we need to keep the user on the same locale that the original app was downloaded in, then `update.locale` file is the only reliable way to keep it. I would love to see us get rid of this file, but I'm not sure if we can.
Attachment #8882000 - Attachment is obsolete: true
(In reply to Zibi Braniecki [:gandalf][:zibi] from comment #15) Ok, it seems we do need to read update.locale, but we should stop doing main thread I/O for it, and stop using NetUtil there. UpdateUtils.Locale is apparently only used by UpdateUtils.formatUpdateURL, which only has 3 callers (+ 2 from tests) : http://searchfox.org/mozilla-central/search?q=formatUpdateURL But this should probably go in a new bug.
Status: ASSIGNED → RESOLVED
Closed: 7 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla56
Depends on: 1380278
Iteration: --- → 56.3 - Jul 24
Performance Impact: --- → P2
Whiteboard: [reserve-photon-performance][qf:p2] → [reserve-photon-performance]
Product: Core → Core Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: