diff options
Diffstat (limited to 'toolkit/components/perfmonitoring/PerformanceStats-content.js')
-rw-r--r-- | toolkit/components/perfmonitoring/PerformanceStats-content.js | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/toolkit/components/perfmonitoring/PerformanceStats-content.js b/toolkit/components/perfmonitoring/PerformanceStats-content.js new file mode 100644 index 000000000..9a6a2d81d --- /dev/null +++ b/toolkit/components/perfmonitoring/PerformanceStats-content.js @@ -0,0 +1,144 @@ +/* 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/. */ + +/** + * A proxy implementing communication between the PerformanceStats.jsm modules + * of the parent and children processes. + * + * This script is loaded in all processes but is essentially a NOOP in the + * parent process. + */ + +"use strict"; + +var { utils: Cu, classes: Cc, interfaces: Ci } = Components; +const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); +const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); + +XPCOMUtils.defineLazyModuleGetter(this, "PerformanceStats", + "resource://gre/modules/PerformanceStats.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Task", + "resource://gre/modules/Task.jsm"); + +/** + * A global performance monitor used by this process. + * + * For the sake of simplicity, rather than attempting to map each PerformanceMonitor + * of the parent to a PerformanceMonitor in each child process, we maintain a single + * PerformanceMonitor in each child process. Probes activation/deactivation for this + * monitor is controlled by the activation/deactivation of probes in the parent. + * + * In the parent, this is always an empty monitor. + */ +var gMonitor = PerformanceStats.getMonitor([]); + +/** + * `true` if this is a content process, `false` otherwise. + */ +var isContent = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT; + +/** + * Handle message `performance-stats-service-acquire`: ensure that the global + * monitor has a given probe. This message must be sent by the parent process + * whenever a probe is activated application-wide. + * + * Note that we may miss acquire messages if they are sent before this process is + * launched. For this reason, `performance-stats-service-collect` automatically + * re-acquires probes if it realizes that they are missing. + * + * This operation is a NOOP on the parent process. + * + * @param {{payload: Array<string>}} msg.data The message received. `payload` + * must be an array of probe names. + */ +Services.cpmm.addMessageListener("performance-stats-service-acquire", function(msg) { + if (!isContent) { + return; + } + let name = msg.data.payload; + ensureAcquired(name); +}); + +/** + * Handle message `performance-stats-service-release`: release a given probe + * from the global monitor. This message must be sent by the parent process + * whenever a probe is deactivated application-wide. + * + * Note that we may miss release messages if they are sent before this process is + * launched. This is ok, as probes are inactive by default: if we miss the release + * message, we have already missed the acquire message, and the effect of both + * messages together is to reset to the default state. + * + * This operation is a NOOP on the parent process. + * + * @param {{payload: Array<string>}} msg.data The message received. `payload` + * must be an array of probe names. + */ +Services.cpmm.addMessageListener("performance-stats-service-release", function(msg) { + if (!isContent) { + return; + } + + // Keep only the probes that do not appear in the payload + let probes = gMonitor.probeNames + .filter(x => msg.data.payload.indexOf(x) == -1); + gMonitor = PerformanceStats.getMonitor(probes); +}); + +/** + * Ensure that this process has all the probes it needs. + * + * @param {Array<string>} probeNames The name of all probes needed by the + * process. + */ +function ensureAcquired(probeNames) { + let alreadyAcquired = gMonitor.probeNames; + + // Algorithm is O(n^2) because we expect that n ≤ 3. + let shouldAcquire = []; + for (let probeName of probeNames) { + if (alreadyAcquired.indexOf(probeName) == -1) { + shouldAcquire.push(probeName) + } + } + + if (shouldAcquire.length == 0) { + return; + } + gMonitor = PerformanceStats.getMonitor([...alreadyAcquired, ...shouldAcquire]); +} + +/** + * Handle message `performance-stats-service-collected`: collect the data + * obtained by the monitor. This message must be sent by the parent process + * whenever we grab a performance snapshot of the application. + * + * This operation provides `null` on the parent process. + * + * @param {{data: {payload: Array<string>}}} msg The message received. `payload` + * must be an array of probe names. + */ +Services.cpmm.addMessageListener("performance-stats-service-collect", Task.async(function*(msg) { + let {id, payload: {probeNames}} = msg.data; + if (!isContent) { + // This message was sent by the parent process to itself. + // As per protocol, respond `null`. + Services.cpmm.sendAsyncMessage("performance-stats-service-collect", { + id, + data: null + }); + return; + } + + // We may have missed acquire messages if the process was loaded too late. + // Catch up now. + ensureAcquired(probeNames); + + // Collect and return data. + let data = yield gMonitor.promiseSnapshot({probeNames}); + Services.cpmm.sendAsyncMessage("performance-stats-service-collect", { + id, + data + }); +})); |