summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/PerformanceCounters.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/components/extensions/PerformanceCounters.sys.mjs161
1 files changed, 161 insertions, 0 deletions
diff --git a/toolkit/components/extensions/PerformanceCounters.sys.mjs b/toolkit/components/extensions/PerformanceCounters.sys.mjs
new file mode 100644
index 0000000000..f5ada161d1
--- /dev/null
+++ b/toolkit/components/extensions/PerformanceCounters.sys.mjs
@@ -0,0 +1,161 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+/* 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/. */
+
+/**
+ * This module contains a global counter to store API call in the current process.
+ */
+
+import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+import { DeferredTask } from "resource://gre/modules/DeferredTask.sys.mjs";
+
+const { DefaultMap } = ExtensionUtils;
+
+const lazy = {};
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "gTimingEnabled",
+ "extensions.webextensions.enablePerformanceCounters",
+ false
+);
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "gTimingMaxAge",
+ "extensions.webextensions.performanceCountersMaxAge",
+ 1000
+);
+
+class CounterMap extends DefaultMap {
+ defaultConstructor() {
+ return new DefaultMap(() => ({ duration: 0, calls: 0 }));
+ }
+
+ flush() {
+ let result = new CounterMap(undefined, this);
+ this.clear();
+ return result;
+ }
+
+ merge(other) {
+ for (let [webextId, counters] of other) {
+ for (let [api, counter] of counters) {
+ let current = this.get(webextId).get(api);
+ current.calls += counter.calls;
+ current.duration += counter.duration;
+ }
+ }
+ }
+}
+
+/**
+ * Global Deferred used to send to the parent performance counters
+ * when the counter is in a child.
+ */
+var _performanceCountersSender = null;
+
+// Pre-definition of the global Counters instance.
+export var PerformanceCounters = null;
+
+function _sendPerformanceCounters(childApiManagerId) {
+ let counters = PerformanceCounters.flush();
+ // No need to send empty counters.
+ if (counters.size == 0) {
+ return;
+ }
+ let options = { childId: childApiManagerId, counters: counters };
+ Services.cpmm.sendAsyncMessage("Extension:SendPerformanceCounter", options);
+}
+
+class Counters {
+ constructor() {
+ this.data = new CounterMap();
+ }
+
+ /**
+ * Returns true if performance counters are enabled.
+ *
+ * Indirection used so gTimingEnabled is not exposed direcly
+ * in PerformanceCounters -- which would prevent tests to dynamically
+ * change the preference value once PerformanceCounters.jsm is loaded.
+ *
+ * @returns {boolean}
+ */
+ get enabled() {
+ return lazy.gTimingEnabled;
+ }
+
+ /**
+ * Returns the counters max age
+ *
+ * Indirection used so gTimingMaxAge is not exposed direcly
+ * in PerformanceCounters -- which would prevent tests to dynamically
+ * change the preference value once PerformanceCounters.jsm is loaded.
+ *
+ * @returns {number}
+ */
+ get maxAge() {
+ return lazy.gTimingMaxAge;
+ }
+
+ /**
+ * Stores an execution time.
+ *
+ * @param {string} webExtensionId The web extension id.
+ * @param {string} apiPath The API path.
+ * @param {integer} duration How long the call took.
+ * @param {childApiManagerId} childApiManagerId If executed from a child, its API manager id.
+ */
+ storeExecutionTime(webExtensionId, apiPath, duration, childApiManagerId) {
+ let apiCounter = this.data.get(webExtensionId).get(apiPath);
+ apiCounter.duration += duration;
+ apiCounter.calls += 1;
+
+ // Create the global deferred task if we're in a child and
+ // it's the first time.
+ if (childApiManagerId) {
+ if (!_performanceCountersSender) {
+ _performanceCountersSender = new DeferredTask(() => {
+ _sendPerformanceCounters(childApiManagerId);
+ }, this.maxAge);
+ }
+ _performanceCountersSender.arm();
+ }
+ }
+
+ /**
+ * Merges another CounterMap into this.data
+ *
+ * Can be used by the main process to merge data received
+ * from the children.
+ *
+ * @param {CounterMap} data The map to merge.
+ */
+ merge(data) {
+ this.data.merge(data);
+ }
+
+ /**
+ * Returns the performance counters and purges them.
+ *
+ * @returns {CounterMap}
+ */
+ flush() {
+ return this.data.flush();
+ }
+
+ /**
+ * Returns the performance counters.
+ *
+ * @returns {CounterMap}
+ */
+ getData() {
+ return this.data;
+ }
+}
+
+PerformanceCounters = new Counters();