summaryrefslogtreecommitdiffstats
path: root/toolkit/components/perfmonitoring/PerformanceStats-content.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/perfmonitoring/PerformanceStats-content.js')
-rw-r--r--toolkit/components/perfmonitoring/PerformanceStats-content.js144
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
+ });
+}));