fune/toolkit/components/extensions/parent/ext-geckoProfiler.js
Philipp Kewisch 52aa5b978e Bug 1507709 - Move WebExtensions geckoProfiler API to toolkit. r=kmag
Differential Revision: https://phabricator.services.mozilla.com/D12100

--HG--
rename : browser/components/extensions/ProfilerGetSymbols-worker.js => toolkit/components/extensions/ProfilerGetSymbols-worker.js
rename : browser/components/extensions/ProfilerGetSymbols.jsm => toolkit/components/extensions/ProfilerGetSymbols.jsm
rename : browser/components/extensions/parent/ext-geckoProfiler.js => toolkit/components/extensions/parent/ext-geckoProfiler.js
rename : browser/components/extensions/profiler_get_symbols.js => toolkit/components/extensions/profiler_get_symbols.js
rename : browser/components/extensions/schemas/geckoProfiler.json => toolkit/components/extensions/schemas/geckoProfiler.json
rename : browser/components/extensions/test/xpcshell/test_ext_geckoProfiler_control.js => toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_control.js
rename : browser/components/extensions/test/xpcshell/test_ext_geckoProfiler_schema.js => toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_schema.js
extra : moz-landing-system : lando
2019-03-22 13:18:01 +00:00

164 lines
5.3 KiB
JavaScript

/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "ProfilerGetSymbols", "resource://gre/modules/ProfilerGetSymbols.jsm");
const PREF_ASYNC_STACK = "javascript.options.asyncstack";
const ASYNC_STACKS_ENABLED = Services.prefs.getBoolPref(PREF_ASYNC_STACK, false);
var {
ExtensionError,
} = ExtensionUtils;
const symbolCache = new Map();
const primeSymbolStore = libs => {
for (const {path, debugName, debugPath, breakpadId} of libs) {
symbolCache.set(`${debugName}/${breakpadId}`, {path, debugPath});
}
};
const isRunningObserver = {
_observers: new Set(),
observe(subject, topic, data) {
switch (topic) {
case "profiler-started":
case "profiler-stopped":
// Call observer(false) or observer(true), but do it through a promise
// so that it's asynchronous.
// We don't want it to be synchronous because of the observer call in
// addObserver, which is asynchronous, and we want to get the ordering
// right.
const isRunningPromise = Promise.resolve(topic === "profiler-started");
for (let observer of this._observers) {
isRunningPromise.then(observer);
}
break;
}
},
_startListening() {
Services.obs.addObserver(this, "profiler-started");
Services.obs.addObserver(this, "profiler-stopped");
},
_stopListening() {
Services.obs.removeObserver(this, "profiler-started");
Services.obs.removeObserver(this, "profiler-stopped");
},
addObserver(observer) {
if (this._observers.size === 0) {
this._startListening();
}
this._observers.add(observer);
observer(Services.profiler.IsActive());
},
removeObserver(observer) {
if (this._observers.delete(observer) && this._observers.size === 0) {
this._stopListening();
}
},
};
this.geckoProfiler = class extends ExtensionAPI {
getAPI(context) {
return {
geckoProfiler: {
async start(options) {
const {bufferSize, windowLength, interval, features, threads} = options;
Services.prefs.setBoolPref(PREF_ASYNC_STACK, false);
if (threads) {
Services.profiler.StartProfiler(bufferSize, interval,
features, features.length,
threads, threads.length,
windowLength);
} else {
Services.profiler.StartProfiler(bufferSize, interval,
features, features.length,
[], 0,
windowLength);
}
},
async stop() {
if (ASYNC_STACKS_ENABLED !== null) {
Services.prefs.setBoolPref(PREF_ASYNC_STACK, ASYNC_STACKS_ENABLED);
}
Services.profiler.StopProfiler();
},
async pause() {
Services.profiler.PauseSampling();
},
async resume() {
Services.profiler.ResumeSampling();
},
async getProfile() {
if (!Services.profiler.IsActive()) {
throw new ExtensionError("The profiler is stopped. " +
"You need to start the profiler before you can capture a profile.");
}
return Services.profiler.getProfileDataAsync();
},
async getProfileAsArrayBuffer() {
if (!Services.profiler.IsActive()) {
throw new ExtensionError("The profiler is stopped. " +
"You need to start the profiler before you can capture a profile.");
}
return Services.profiler.getProfileDataAsArrayBuffer();
},
async getSymbols(debugName, breakpadId) {
if (symbolCache.size === 0) {
primeSymbolStore(Services.profiler.sharedLibraries);
}
const cachedLibInfo = symbolCache.get(`${debugName}/${breakpadId}`);
if (!cachedLibInfo) {
throw new Error(
`The library ${debugName} ${breakpadId} is not in the Services.profiler.sharedLibraries list, ` +
"so the local path for it is not known and symbols for it can not be obtained. " +
"This usually happens if a content process uses a library that's not used in the parent " +
"process - Services.profiler.sharedLibraries only knows about libraries in the parent process.");
}
const {path, debugPath} = cachedLibInfo;
if (!OS.Path.split(path).absolute) {
throw new Error(
`Services.profiler.sharedLibraries did not contain an absolute path for the library ${debugName} ${breakpadId}, ` +
"so symbols for this library can not be obtained.");
}
return ProfilerGetSymbols.getSymbolTable(path, debugPath, breakpadId);
},
onRunning: new EventManager({
context,
name: "geckoProfiler.onRunning",
register: fire => {
isRunningObserver.addObserver(fire.async);
return () => {
isRunningObserver.removeObserver(fire.async);
};
},
}).api(),
},
};
}
};