mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 17:25:36 +00:00
Bug 1307227 - Copy over the Gecko Profiler Addon files; r=julienw
This bug's first commit will be a non-working revision.
These files are taken from:
68d5de9c9f
Differential Revision: https://phabricator.services.mozilla.com/D31547
--HG--
extra : moz-landing-system : lando
This commit is contained in:
parent
72973df697
commit
f59b172f85
306
devtools/client/performance-new/popup/background.jsm
Normal file
306
devtools/client/performance-new/popup/background.jsm
Normal file
@ -0,0 +1,306 @@
|
||||
/* global browser */
|
||||
|
||||
const DEFAULT_VIEWER_URL = "https://profiler.firefox.com";
|
||||
const DEFAULT_WINDOW_LENGTH = 20; // 20sec
|
||||
|
||||
const tabToConnectionMap = new Map();
|
||||
|
||||
var profilerState;
|
||||
var profileViewerURL = DEFAULT_VIEWER_URL;
|
||||
var isMigratedToNewUrl;
|
||||
|
||||
function adjustState(newState) {
|
||||
// Deep clone the object, since this can be called through popup.html,
|
||||
// which can be unloaded thus leaving this object dead.
|
||||
newState = JSON.parse(JSON.stringify(newState));
|
||||
Object.assign(window.profilerState, newState);
|
||||
browser.storage.local.set({ profilerState: window.profilerState });
|
||||
}
|
||||
|
||||
function makeProfileAvailableToTab(profile, port) {
|
||||
port.postMessage({ type: "ProfilerConnectToPage", payload: profile });
|
||||
|
||||
port.onMessage.addListener(async message => {
|
||||
if (message.type === "ProfilerGetSymbolTable") {
|
||||
const { debugName, breakpadId } = message;
|
||||
try {
|
||||
const [
|
||||
addresses,
|
||||
index,
|
||||
buffer,
|
||||
] = await browser.geckoProfiler.getSymbols(debugName, breakpadId);
|
||||
|
||||
port.postMessage({
|
||||
type: "ProfilerGetSymbolTableReply",
|
||||
status: "success",
|
||||
result: [addresses, index, buffer],
|
||||
debugName,
|
||||
breakpadId,
|
||||
});
|
||||
} catch (e) {
|
||||
port.postMessage({
|
||||
type: "ProfilerGetSymbolTableReply",
|
||||
status: "error",
|
||||
error: `${e}`,
|
||||
debugName,
|
||||
breakpadId,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function createAndWaitForTab(url) {
|
||||
const tabPromise = browser.tabs.create({
|
||||
active: true,
|
||||
url,
|
||||
});
|
||||
|
||||
return tabPromise;
|
||||
}
|
||||
|
||||
function getProfilePreferablyAsArrayBuffer() {
|
||||
// This is a compatibility wrapper for Firefox builds from before 1362800
|
||||
// landed. We can remove it once Nightly switches to 56.
|
||||
if ("getProfileAsArrayBuffer" in browser.geckoProfiler) {
|
||||
return browser.geckoProfiler.getProfileAsArrayBuffer();
|
||||
}
|
||||
return browser.geckoProfiler.getProfile();
|
||||
}
|
||||
|
||||
async function captureProfile() {
|
||||
// Pause profiler before we collect the profile, so that we don't capture
|
||||
// more samples while the parent process waits for subprocess profiles.
|
||||
await browser.geckoProfiler.pause().catch(() => {});
|
||||
|
||||
const profilePromise = getProfilePreferablyAsArrayBuffer().catch(
|
||||
e => {
|
||||
console.error(e);
|
||||
return {};
|
||||
}
|
||||
);
|
||||
|
||||
const tabOpenPromise = createAndWaitForTab(profileViewerURL + "/from-addon");
|
||||
|
||||
try {
|
||||
const [profile, tab] = await Promise.all([profilePromise, tabOpenPromise]);
|
||||
|
||||
const connection = tabToConnectionMap.get(tab.id);
|
||||
|
||||
if (connection) {
|
||||
// If, for instance, it takes a long time to load the profile,
|
||||
// then our onDOMContentLoaded handler and our runtime.onConnect handler
|
||||
// have already connected to the page. All we need to do then is
|
||||
// provide the profile.
|
||||
makeProfileAvailableToTab(profile, connection.port);
|
||||
} else {
|
||||
// If our onDOMContentLoaded handler and our runtime.onConnect handler
|
||||
// haven't connected to the page, set this so that they'll have a
|
||||
// profile they can provide once they do.
|
||||
tabToConnectionMap.set(tab.id, { profile });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// const { tab } = await tabOpenPromise;
|
||||
// TODO data URL doesn't seem to be working. Permissions issue?
|
||||
// await browser.tabs.update(tab.id, { url: `data:text/html,${encodeURIComponent(e.toString)}` });
|
||||
}
|
||||
|
||||
try {
|
||||
await browser.geckoProfiler.resume();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not all features are supported on every version of Firefox. Get the list of checked
|
||||
* features, add a few defaults, and filter for what is actually supported.
|
||||
*/
|
||||
function getEnabledFeatures(features, threads) {
|
||||
const enabledFeatures = Object.keys(features).filter(f => features[f]);
|
||||
if (threads.length > 0) {
|
||||
enabledFeatures.push("threads");
|
||||
}
|
||||
const supportedFeatures = Object.values(
|
||||
browser.geckoProfiler.ProfilerFeature
|
||||
);
|
||||
return enabledFeatures.filter(feature => supportedFeatures.includes(feature));
|
||||
}
|
||||
|
||||
async function startProfiler() {
|
||||
const settings = window.profilerState;
|
||||
const threads = settings.threads.split(",");
|
||||
const options = {
|
||||
bufferSize: settings.buffersize,
|
||||
interval: settings.interval,
|
||||
features: getEnabledFeatures(settings.features, threads),
|
||||
threads,
|
||||
};
|
||||
if (
|
||||
browser.geckoProfiler.supports &&
|
||||
browser.geckoProfiler.supports.WINDOWLENGTH
|
||||
) {
|
||||
options.windowLength =
|
||||
settings.windowLength !== settings.infiniteWindowLength
|
||||
? settings.windowLength
|
||||
: 0;
|
||||
}
|
||||
await browser.geckoProfiler.start(options);
|
||||
}
|
||||
|
||||
async function stopProfiler() {
|
||||
await browser.geckoProfiler.stop();
|
||||
}
|
||||
|
||||
/* exported restartProfiler */
|
||||
async function restartProfiler() {
|
||||
await stopProfiler();
|
||||
await startProfiler();
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const storageResults = await browser.storage.local.get({
|
||||
profilerState: null,
|
||||
profileViewerURL: DEFAULT_VIEWER_URL,
|
||||
// This value is to check whether or not folks have been migrated from
|
||||
// perf-html.io to profiler.firefox.com
|
||||
isMigratedToNewUrl: false,
|
||||
});
|
||||
|
||||
// Assign to global variables:
|
||||
window.profilerState = storageResults.profilerState;
|
||||
window.profileViewerURL = storageResults.profileViewerURL;
|
||||
|
||||
if (!storageResults.isMigratedToNewUrl) {
|
||||
if (window.profileViewerURL.startsWith("https://perf-html.io")) {
|
||||
// This user needs to be migrated from perf-html.io to profiler.firefox.com.
|
||||
// This is only done one time.
|
||||
window.profileViewerURL = DEFAULT_VIEWER_URL;
|
||||
}
|
||||
// Store the fact that this migration check has been done, and optionally update
|
||||
// the url if it was changed.
|
||||
await browser.storage.local.set({
|
||||
isMigratedToNewUrl: true,
|
||||
profileViewerURL: window.profileViewerURL,
|
||||
});
|
||||
}
|
||||
|
||||
if (!window.profilerState) {
|
||||
window.profilerState = {};
|
||||
|
||||
const features = {
|
||||
java: false,
|
||||
js: true,
|
||||
leaf: true,
|
||||
mainthreadio: false,
|
||||
memory: false,
|
||||
privacy: false,
|
||||
responsiveness: true,
|
||||
screenshots: false,
|
||||
seqstyle: false,
|
||||
stackwalk: true,
|
||||
tasktracer: false,
|
||||
trackopts: false,
|
||||
jstracer: false,
|
||||
};
|
||||
|
||||
const platform = await browser.runtime.getPlatformInfo();
|
||||
switch (platform.os) {
|
||||
case "mac":
|
||||
// Screenshots are currently only working on mac.
|
||||
features.screenshots = true;
|
||||
break;
|
||||
case "android":
|
||||
// Java profiling is only meaningful on android.
|
||||
features.java = true;
|
||||
break;
|
||||
}
|
||||
|
||||
adjustState({
|
||||
isRunning: false,
|
||||
settingsOpen: false,
|
||||
buffersize: 10000000, // 90MB
|
||||
windowLength: DEFAULT_WINDOW_LENGTH,
|
||||
interval: 1,
|
||||
features,
|
||||
threads: "GeckoMain,Compositor",
|
||||
});
|
||||
} else if (window.profilerState.windowLength === undefined) {
|
||||
// We have `windowprofilerState` but no `windowLength`.
|
||||
// That means we've upgraded the gecko profiler addon from an older version.
|
||||
// Adding the default window legth in that case.
|
||||
adjustState({
|
||||
windowLength: DEFAULT_WINDOW_LENGTH,
|
||||
});
|
||||
}
|
||||
|
||||
browser.geckoProfiler.onRunning.addListener(isRunning => {
|
||||
adjustState({ isRunning });
|
||||
|
||||
// With "path: null" we'll get the default icon for the browser action, which
|
||||
// is theme-aware.
|
||||
// The on state does not need to be theme-aware because we want to highlight
|
||||
// the icon in blue regardless of whether a dark or a light theme is in use.
|
||||
browser.browserAction.setIcon({
|
||||
path: isRunning ? "icons/toolbar_on.png" : null,
|
||||
});
|
||||
|
||||
browser.browserAction.setTitle({
|
||||
title: isRunning ? "Gecko Profiler (on)" : null,
|
||||
});
|
||||
|
||||
for (const popup of browser.extension.getViews({ type: "popup" })) {
|
||||
popup.renderState(window.profilerState);
|
||||
}
|
||||
});
|
||||
|
||||
browser.storage.onChanged.addListener(changes => {
|
||||
if (changes.profileViewerURL) {
|
||||
profileViewerURL = changes.profileViewerURL.newValue;
|
||||
}
|
||||
});
|
||||
|
||||
browser.commands.onCommand.addListener(command => {
|
||||
if (command === "ToggleProfiler") {
|
||||
if (window.profilerState.isRunning) {
|
||||
stopProfiler();
|
||||
} else {
|
||||
startProfiler();
|
||||
}
|
||||
} else if (command === "CaptureProfile") {
|
||||
if (window.profilerState.isRunning) {
|
||||
captureProfile();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
browser.runtime.onConnect.addListener(port => {
|
||||
const tabId = port.sender.tab.id;
|
||||
const connection = tabToConnectionMap.get(tabId);
|
||||
if (connection && connection.profile) {
|
||||
makeProfileAvailableToTab(connection.profile, port);
|
||||
} else {
|
||||
tabToConnectionMap.set(tabId, { port });
|
||||
}
|
||||
});
|
||||
|
||||
browser.tabs.onRemoved.addListener(tabId => {
|
||||
tabToConnectionMap.delete(tabId);
|
||||
});
|
||||
|
||||
browser.webNavigation.onDOMContentLoaded.addListener(
|
||||
async ({ frameId, tabId, url }) => {
|
||||
if (frameId !== 0) {
|
||||
return;
|
||||
}
|
||||
if (url.startsWith(profileViewerURL)) {
|
||||
browser.tabs.executeScript(tabId, { file: "content.js" });
|
||||
} else {
|
||||
// As soon as we navigate away from the profile report, clean
|
||||
// this up so we don't leak it.
|
||||
tabToConnectionMap.delete(tabId);
|
||||
}
|
||||
}
|
||||
);
|
||||
})();
|
@ -0,0 +1,11 @@
|
||||
<!-- 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 file was taken from command-screenshot.svg from the Firefox devtools.
|
||||
- The camera metaphor doesn't really work well, and the fact that we're
|
||||
- re-using an icon for a completely different use isn't great either. If
|
||||
- anybody has any ideas for a replacement, I'd love to hear them. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#0b0b0b">
|
||||
<path d="M13.6 5H11V3.5C11 2.7 10 2 9.2 2H6.8C6 2 5 2.7 5 3.5V5H2.4C1.6 5 1 5.6 1 6.4v6.5c0 .8.6 1.1 1.4 1.1h11.2c.8 0 1.4-.3 1.4-1.1V6.4c0-.8-.6-1.4-1.4-1.4zm.4 8H2V6h4V3h4v3h3.9l.1 7z"/>
|
||||
<path d="M8 6.8c-1.3 0-2.4 1.1-2.4 2.4s1.1 2.4 2.4 2.4 2.4-1.1 2.4-2.4c0-1.3-1.1-2.4-2.4-2.4zm0 3.5c-.7 0-1.2-.5-1.2-1.1S7.3 8.1 8 8.1s1.2.5 1.2 1.1-.5 1.1-1.2 1.1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 960 B |
407
devtools/client/performance-new/popup/popup.css
Normal file
407
devtools/client/performance-new/popup/popup.css
Normal file
@ -0,0 +1,407 @@
|
||||
html {
|
||||
background: rgb(250,250,250);
|
||||
font: message-box;
|
||||
-moz-user-select: none;
|
||||
cursor: default;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
* {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
/**
|
||||
* We had to use `max-width` instead of `width` here because of a bug on Firefox
|
||||
* that causes a horizontal scrollbar when there is a vertical scrollbar on Linux.
|
||||
* This is not a problem on platforms that have a floating scrollbar that doesn't
|
||||
* take additional space, but on platforms that we don't have floating scrollbar,
|
||||
* it takes some space from the body element and causes horizontal scrollbar to appear.
|
||||
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=1400279
|
||||
*/
|
||||
max-width: 32em;
|
||||
}
|
||||
|
||||
.status-display {
|
||||
margin: 0;
|
||||
padding: 10px 4px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.status-display::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
vertical-align: top;
|
||||
margin: -2px 3px -20px;
|
||||
}
|
||||
|
||||
:root.status-stopped .status-display:not(.status-display-stopped) { display: none; }
|
||||
:root.status-running .status-display:not(.status-display-running) { display: none; }
|
||||
|
||||
.status-display-stopped {
|
||||
background: hsl(210, 80%, 85%);
|
||||
}
|
||||
|
||||
.status-display-running {
|
||||
background: hsl(90, 70%, 70%);
|
||||
}
|
||||
|
||||
.status-display-stopped::before {
|
||||
background: hsl(210, 70%, 60%);
|
||||
}
|
||||
|
||||
.status-display-running::before {
|
||||
background: hsl(90, 80%, 40%);
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.status-display-button {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 7px;
|
||||
padding: 2.5px;
|
||||
border: 0.5px solid transparent;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.status-display-button:hover {
|
||||
box-shadow: inset 0 0.5px hsla(0,0%,100%,0.3);
|
||||
color: black;
|
||||
}
|
||||
|
||||
.status-display-button:hover:active {
|
||||
box-shadow: inset 0 2px 5px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.button-start {
|
||||
border-color: hsl(210, 30%, 60%);
|
||||
background: rgba(0,0,0,0.08);
|
||||
color: hsl(210, 80%, 20%);
|
||||
}
|
||||
|
||||
.button-cancel {
|
||||
border-color: hsl(90, 40%, 50%);
|
||||
background: rgba(0,0,0,0.08);
|
||||
color: hsl(90, 80%, 20%);
|
||||
}
|
||||
|
||||
.button-start:hover {
|
||||
border-color: hsl(90, 40%, 50%);
|
||||
background: linear-gradient(hsl(90, 70%, 68%), hsl(90, 70%, 58%));
|
||||
color: hsl(90, 80%, 20%);
|
||||
}
|
||||
|
||||
.button-cancel:hover {
|
||||
background: linear-gradient(hsl(0, 95%, 68%), hsl(0, 95%, 58%));
|
||||
border-color: hsl(0, 90%, 30%);
|
||||
}
|
||||
|
||||
.button-start:hover:active {
|
||||
background: linear-gradient(hsl(90, 70%, 50%), hsl(90, 70%, 45%));
|
||||
}
|
||||
|
||||
.button-cancel:hover:active {
|
||||
background: linear-gradient(hsl(0, 90%, 50%), hsl(0, 90%, 45%));
|
||||
}
|
||||
|
||||
#button-capture {
|
||||
margin: 6px 5px 5px;
|
||||
height: auto;
|
||||
text-align: left;
|
||||
padding: 5px 2px;
|
||||
padding-left: 32px;
|
||||
border: 1px solid #DDD;
|
||||
box-shadow: 0 1px rgba(0,0,0,0.3);
|
||||
border-radius: 5px;
|
||||
background: linear-gradient(#FAFAFA, #EEE);
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:root.status-stopped #button-capture {
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
:root.status-running #button-capture:hover {
|
||||
background: linear-gradient(#EEE, #DDD);
|
||||
}
|
||||
|
||||
:root.status-running #button-capture:hover:active {
|
||||
background: #CCC;
|
||||
border-color: #BBB;
|
||||
}
|
||||
|
||||
#capture-label {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#capture-label::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url(icons/capture-profile-icon.svg);
|
||||
top: 6px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
:root.status-stopped #capture-label::before {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.keyboard-hint {
|
||||
font-size: 1rem;
|
||||
font-weight: normal;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
kbd {
|
||||
font-size: 10px;
|
||||
background-color: hsla(0,0%,100%,0.4);
|
||||
border: 1px solid #CCC;
|
||||
border-radius: 0.2em;
|
||||
display: inline-block;
|
||||
padding: 0.1em 0.35em;
|
||||
box-shadow: 0 0.1em 0 #BBB;
|
||||
margin: 0 0.15em;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
#help-capture {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.info-density {
|
||||
margin: 4px 12px;
|
||||
display: grid;
|
||||
grid-template-columns: 7em auto;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
|
||||
.info-density > dt {
|
||||
grid-column: 1;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.info-density > dd {
|
||||
grid-column: 2;
|
||||
margin: 0;
|
||||
padding: 5px 0 3px;
|
||||
}
|
||||
|
||||
.settings {
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
.settings:not(.open) > .settings-content,
|
||||
.settings:not(.open) > .settings-apply-button-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#settings-label {
|
||||
margin: 0;
|
||||
padding: 4px 10px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
#settings-label:hover {
|
||||
background: #DDD;
|
||||
}
|
||||
|
||||
#settings-label:hover:active {
|
||||
background: #CCC;
|
||||
}
|
||||
|
||||
#settings-label::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 9px solid #BBB;
|
||||
border-top: 5px solid transparent;
|
||||
border-bottom: 5px solid transparent;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.settings.open > #settings-label::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.discrete-level {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.discrete-level-notch {
|
||||
flex: 1;
|
||||
margin-right: 1px;
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.discrete-level-notch.normal.active {
|
||||
border-color: hsl(90, 90%, 40%);
|
||||
background-color: hsla(90, 90%, 40%, 0.5);
|
||||
}
|
||||
|
||||
.discrete-level-notch.warning.active {
|
||||
border-color: hsl(45, 100%, 49%);
|
||||
background-color: hsla(45, 100%, 49%, 0.5);
|
||||
}
|
||||
|
||||
.discrete-level-notch.critical.active {
|
||||
border-color: hsl(0, 90%, 40%);
|
||||
background-color: hsla(0, 90%, 40%, 0.5);
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
margin: 0;
|
||||
padding: 0 10px 18px;
|
||||
line-height: 22px;
|
||||
display: grid;
|
||||
grid-template-columns: 8em auto;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
|
||||
.settings-setting-label {
|
||||
margin: 0;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.range-with-value {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
body.no-windowlength .settings-setting-label.windowlength,
|
||||
body.no-windowlength .range-with-value.windowlength {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.range-input {
|
||||
margin: 0;
|
||||
width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.range-value {
|
||||
margin-left: 10px;
|
||||
width: 4em;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.settings-textbox {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.features-list {
|
||||
margin: 0;
|
||||
padding: 2px 0 0;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.features-list > li {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.settings-apply-button-wrapper {
|
||||
padding: 4px 10px;
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.perf-settings-row {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.perf-settings-row.focused {
|
||||
background-color: #0074e8;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.perf-settings-text-input {
|
||||
width: 100%;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.perf-settings-text-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.perf-settings-details-contents {
|
||||
padding: 4px;
|
||||
margin: 0 0 18px;
|
||||
border: #ededf0 1px solid;
|
||||
background-color: #f9f9fa;
|
||||
}
|
||||
|
||||
.perf-settings-details {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 3;
|
||||
}
|
||||
|
||||
.perf-settings-summary {
|
||||
height: 30px;
|
||||
cursor: default;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.perf-settings-thread-columns {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.perf-settings-thread-column {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.perf-settings-checkbox-label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.perf-settings-feature-label {
|
||||
margin: 8px 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.perf-settings-checkbox {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.perf-settings-feature-title {
|
||||
margin-left: 20px;
|
||||
flex: 1 100%;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.perf-settings-feature-name {
|
||||
color: #0060df;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.perf-settings-subtext {
|
||||
font-weight: bold;
|
||||
}
|
178
devtools/client/performance-new/popup/popup.html
Normal file
178
devtools/client/performance-new/popup/popup.html
Normal file
@ -0,0 +1,178 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="status-running">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="popup.css">
|
||||
</head>
|
||||
<body>
|
||||
<p class="status-display status-display-running">
|
||||
The profiler is recording.
|
||||
<input type="button" class="status-display-button button-cancel" value="Discard & Stop">
|
||||
</p>
|
||||
|
||||
<p class="status-display status-display-stopped">
|
||||
The profiler is stopped.
|
||||
<input type="button" class="status-display-button button-start" value="Start">
|
||||
</p>
|
||||
|
||||
<button id="button-capture">
|
||||
<strong id="capture-label">Capture Profile <span class="keyboard-hint"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>2</kbd></span></strong>
|
||||
<p id="help-capture">Capture the current contents of the profile buffer and open the profile in a new tab.</p>
|
||||
</button>
|
||||
|
||||
<dl class="info-density">
|
||||
<dt>Overhead:</dt>
|
||||
<dd>
|
||||
<div class="discrete-level">
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch normal active"></span>
|
||||
<span class="discrete-level-notch warning active"></span>
|
||||
<span class="discrete-level-notch warning active"></span>
|
||||
<span class="discrete-level-notch warning active"></span>
|
||||
<span class="discrete-level-notch warning active"></span>
|
||||
<span class="discrete-level-notch warning inactive"></span>
|
||||
<span class="discrete-level-notch warning inactive"></span>
|
||||
<span class="discrete-level-notch warning inactive"></span>
|
||||
<span class="discrete-level-notch critical inactive"></span>
|
||||
<span class="discrete-level-notch critical inactive"></span>
|
||||
<span class="discrete-level-notch critical inactive"></span>
|
||||
<span class="discrete-level-notch critical inactive"></span>
|
||||
<span class="discrete-level-notch critical inactive"></span>
|
||||
<span class="discrete-level-notch critical inactive"></span>
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<section class="settings">
|
||||
<h1 id="settings-label">Settings</h1>
|
||||
<section class="settings-content">
|
||||
<h1 class="settings-setting-label">Interval:</h1>
|
||||
<span class="range-with-value">
|
||||
<input type="range" class="range-input interval-range" min="0" max="100">
|
||||
<span class="range-value interval-value">1 ms</span>
|
||||
</span>
|
||||
<h1 class="settings-setting-label windowlength">Window length:</h1>
|
||||
<span class="range-with-value windowlength">
|
||||
<input type="range" class="range-input windowlength-range" min="0" max="100">
|
||||
<span class="range-value windowlength-value">20 sec</span>
|
||||
</span>
|
||||
<h1 class="settings-setting-label">Buffer size:</h1>
|
||||
<span class="range-with-value">
|
||||
<input type="range" class="range-input buffersize-range" min="0" max="100">
|
||||
<span class="range-value buffersize-value">90 MB</span>
|
||||
</span>
|
||||
<div class="perf-settings-details">
|
||||
<summary class="perf-settings-summary" id="perf-settings-threads-summary">Threads:</summary>
|
||||
<div class="perf-settings-details-contents">
|
||||
<div class="perf-settings-thread-columns">
|
||||
<div class="perf-settings-thread-column"><label class="perf-settings-checkbox-label" title="The main processes for both the parent process, and content processes"><input
|
||||
class="perf-settings-checkbox" id="perf-settings-thread-checkbox-gecko-main" type="checkbox" value="GeckoMain" />GeckoMain</label><label
|
||||
class="perf-settings-checkbox-label" title="Composites together different painted elements on the page."><input
|
||||
class="perf-settings-checkbox" id="perf-settings-thread-checkbox-compositor" type="checkbox" value="Compositor" />Compositor</label><label
|
||||
class="perf-settings-checkbox-label" title="This handle both web workers and service workers"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-thread-checkbox-dom-worker" type="checkbox" value="DOM Worker" />DOM Worker</label><label
|
||||
class="perf-settings-checkbox-label" title="When WebRender is enabled, the thread that executes OpenGL calls"><input
|
||||
class="perf-settings-checkbox" id="perf-settings-thread-checkbox-renderer" type="checkbox" value="Renderer" />Renderer</label></div>
|
||||
<div class="perf-settings-thread-column"><label class="perf-settings-checkbox-label" title="The WebRender RenderBackend thread"><input
|
||||
class="perf-settings-checkbox" id="perf-settings-thread-checkbox-render-backend" type="checkbox" value="RenderBackend" />RenderBackend</label><label
|
||||
class="perf-settings-checkbox-label" title="When off-main-thread painting is enabled, the thread on which painting happens"><input
|
||||
class="perf-settings-checkbox" id="perf-settings-thread-checkbox-paint-worker" type="checkbox" value="PaintWorker" />PaintWorker</label><label
|
||||
class="perf-settings-checkbox-label" title="Style computation is split into multiple threads"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-thread-checkbox-style-thread" type="checkbox" value="StyleThread" />StyleThread</label><label
|
||||
class="perf-settings-checkbox-label" title="The thread where networking code runs any blocking socket calls"><input
|
||||
class="perf-settings-checkbox" id="perf-settings-thread-checkbox-socket-thread" type="checkbox" value="Socket Thread" />Socket
|
||||
Thread</label></div>
|
||||
<div class="perf-settings-thread-column"><label class="perf-settings-checkbox-label" title="TODO"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-thread-checkbox-stream-trans" type="checkbox" value="StreamTrans" />StreamTrans</label><label
|
||||
class="perf-settings-checkbox-label" title="Image decoding threads"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-thread-checkbox-img-decoder" type="checkbox" value="ImgDecoder" />ImgDecoder</label><label
|
||||
class="perf-settings-checkbox-label" title="DNS resolution happens on this thread"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-thread-checkbox-dns-resolver" type="checkbox" value="DNS Resolver" />DNS Resolver</label></div>
|
||||
</div>
|
||||
<div class="perf-settings-row"><label class="perf-settings-text-label" title="These thread names are a comma separated list that is used to enable profiling of the threads in the profiler. The name needs to be only a partial match of the thread name to be included. It is whitespace sensitive.">
|
||||
<div>Add custom threads by name:</div><input class="perf-settings-text-input" id="perf-settings-thread-text"
|
||||
type="text" value="GeckoMain,Compositor" />
|
||||
</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="perf-settings-details">
|
||||
<summary class="perf-settings-summary" id="perf-settings-features-summary">Features:</summary>
|
||||
<div class="perf-settings-details-contents">
|
||||
<label class="perf-settings-checkbox-label perf-settings-feature-label"><input
|
||||
class="perf-settings-checkbox" id="perf-settings-feature-checkbox-stackwalk" type="checkbox" value="stackwalk" />
|
||||
<div class="perf-settings-feature-name">Native Stacks</div>
|
||||
<div class="perf-settings-feature-title">Record native stacks (C++ and Rust). This is not available on all
|
||||
platforms.<span class="perf-settings-subtext"> (Recommended on by default.)</span></div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-js" type="checkbox" value="js" />
|
||||
<div class="perf-settings-feature-name">JavaScript</div>
|
||||
<div class="perf-settings-feature-title">Record JavaScript stack information, and interleave it with native
|
||||
stacks.<span class="perf-settings-subtext"> (Recommended on by default.)</span></div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label" id="java"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-java" type="checkbox" value="java" />
|
||||
<div class="perf-settings-feature-name">Java</div>
|
||||
<div class="perf-settings-feature-title">Profile Java code.</div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-responsiveness" type="checkbox" value="responsiveness" />
|
||||
<div class="perf-settings-feature-name">Responsiveness</div>
|
||||
<div class="perf-settings-feature-title">Collect thread responsiveness information.<span class="perf-settings-subtext">
|
||||
(Recommended on by default.)</span></div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-leaf" type="checkbox" value="leaf" />
|
||||
<div class="perf-settings-feature-name">Native Leaf Stack</div>
|
||||
<div class="perf-settings-feature-title">Record the native memory address of the leaf-most stack. This could be
|
||||
useful on platforms that do not support stack walking.<span class="perf-settings-subtext"> (Recommended on by
|
||||
default.)</span></div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-screenshots" type="checkbox" value="screenshots" />
|
||||
<div class="perf-settings-feature-name">Screenshots</div>
|
||||
<div class="perf-settings-feature-title">Capture screenshots of browser windows.</div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-mainthreadio" type="checkbox" value="mainthreadio" />
|
||||
<div class="perf-settings-feature-name">Main Thread IO</div>
|
||||
<div class="perf-settings-feature-title">Record main thread I/O markers.</div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-memory" type="checkbox" value="memory" />
|
||||
<div class="perf-settings-feature-name">Memory</div>
|
||||
<div class="perf-settings-feature-title">Add memory measurements to the samples, this includes resident set
|
||||
size (RSS) and unique set size (USS).</div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-privacy" type="checkbox" value="privacy" />
|
||||
<div class="perf-settings-feature-name">Privacy</div>
|
||||
<div class="perf-settings-feature-title">Remove some potentially user-identifiable information.</div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-seqstyle" type="checkbox" value="seqstyle" />
|
||||
<div class="perf-settings-feature-name">Sequential Styling</div>
|
||||
<div class="perf-settings-feature-title">Disable parallel traversal in styling.</div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-trackopts" type="checkbox" value="trackopts" />
|
||||
<div class="perf-settings-feature-name">JIT Optimizations</div>
|
||||
<div class="perf-settings-feature-title">Track JIT optimizations in the JS engine.</div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-tasktracer" type="checkbox" value="tasktracer" />
|
||||
<div class="perf-settings-feature-name">TaskTracer</div>
|
||||
<div class="perf-settings-feature-title">Enable TaskTracer (Experimental, requires custom build.)</div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-jstracer" type="checkbox" value="jstracer" />
|
||||
<div class="perf-settings-feature-name">JSTracer</div>
|
||||
<div class="perf-settings-feature-title">Trace JS engine (Experimental, requires custom build.)</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="settings-apply-button-wrapper"><input type="button" class="settings-apply-button" value="Apply (Restart Profiler)"></section>
|
||||
</section>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
385
devtools/client/performance-new/popup/popup.js
Normal file
385
devtools/client/performance-new/popup/popup.js
Normal file
@ -0,0 +1,385 @@
|
||||
/* global browser */
|
||||
const intervalScale = makeExponentialScale(0.01, 100);
|
||||
const buffersizeScale = makeExponentialScale(10000, 100000000);
|
||||
// Window Length accepts a numerical value between 1-N. We also need to put an
|
||||
// infinite number at the end of the window length slider. Therefore, the max
|
||||
// value pretends like it's infinite in the slider.
|
||||
// The maximum value of window length is 300 seconds. For that reason, we are
|
||||
// treating 400 as infinity.
|
||||
const infiniteWindowLength = 400;
|
||||
const windowLengthScale = makeExponentialScale(1, infiniteWindowLength);
|
||||
|
||||
const PROFILE_ENTRY_SIZE = 9; // sizeof(double) + sizeof(char), http://searchfox.org/mozilla-central/rev/e8835f52eff29772a57dca7bcc86a9a312a23729/tools/profiler/core/ProfileEntry.h#73
|
||||
|
||||
const featurePrefix = "perf-settings-feature-checkbox-";
|
||||
const features = [
|
||||
"java",
|
||||
"js",
|
||||
"leaf",
|
||||
"mainthreadio",
|
||||
"memory",
|
||||
"privacy",
|
||||
"responsiveness",
|
||||
"screenshots",
|
||||
"seqstyle",
|
||||
"stackwalk",
|
||||
"tasktracer",
|
||||
"jstracer",
|
||||
"trackopts",
|
||||
];
|
||||
const threadPrefix = "perf-settings-thread-checkbox-";
|
||||
// A map of element ID suffixes to their corresponding profile thread name, for
|
||||
// creating ID -> name mappings, e.g.`$threadPrefix}dom-worker` - DOM Worker.
|
||||
const threadMap = {
|
||||
compositor: "Compositor",
|
||||
"dns-resolver": "DNS Resolver",
|
||||
"dom-worker": "DOM Worker",
|
||||
"gecko-main": "GeckoMain",
|
||||
"img-decoder": "ImgDecoder",
|
||||
"paint-worker": "PaintWorker",
|
||||
"render-backend": "RenderBackend",
|
||||
renderer: "Renderer",
|
||||
"socket-thread": "Socket Thread",
|
||||
"stream-trans": "StreamTrans",
|
||||
"style-thread": "StyleThread",
|
||||
};
|
||||
|
||||
function renderState(state) {
|
||||
const { isRunning, settingsOpen, interval, buffersize, windowLength } = state;
|
||||
document.documentElement.classList.toggle("status-running", isRunning);
|
||||
document.documentElement.classList.toggle("status-stopped", !isRunning);
|
||||
document.querySelector(".settings").classList.toggle("open", settingsOpen);
|
||||
document.querySelector(".interval-value").textContent = `${interval} ms`;
|
||||
document.querySelector(".buffersize-value").textContent = prettyBytes(
|
||||
buffersize * PROFILE_ENTRY_SIZE
|
||||
);
|
||||
document.querySelector(".windowlength-value").textContent = windowLength ===
|
||||
infiniteWindowLength
|
||||
? `∞`
|
||||
: `${windowLength} sec`;
|
||||
const overhead = calculateOverhead(state);
|
||||
const overheadDiscreteContainer = document.querySelector(".discrete-level");
|
||||
for (let i = 0; i < overheadDiscreteContainer.children.length; i++) {
|
||||
const discreteLevelNotch = overheadDiscreteContainer.children[i];
|
||||
const isActive =
|
||||
i <=
|
||||
Math.round(overhead * (overheadDiscreteContainer.children.length - 1));
|
||||
discreteLevelNotch.classList.toggle("active", isActive);
|
||||
discreteLevelNotch.classList.toggle("inactive", !isActive);
|
||||
}
|
||||
|
||||
renderControls(state);
|
||||
}
|
||||
|
||||
function renderControls(state) {
|
||||
document.querySelector(".interval-range").value =
|
||||
intervalScale.fromValueToFraction(state.interval) * 100;
|
||||
document.querySelector(".buffersize-range").value =
|
||||
buffersizeScale.fromValueToFraction(state.buffersize) * 100;
|
||||
document.querySelector(".windowlength-range").value =
|
||||
windowLengthScale.fromValueToFraction(state.windowLength) * 100;
|
||||
|
||||
for (let name of features) {
|
||||
document.getElementById(featurePrefix + name).value = state[name];
|
||||
}
|
||||
|
||||
for (let name in threadMap) {
|
||||
document.getElementById(
|
||||
threadPrefix + name
|
||||
).checked = state.threads.includes(threadMap[name]);
|
||||
}
|
||||
|
||||
document.querySelector("#perf-settings-thread-text").value = state.threads;
|
||||
}
|
||||
|
||||
function clamp(val, min, max) {
|
||||
return Math.max(min, Math.min(max, val));
|
||||
}
|
||||
|
||||
function lerp(frac, rangeStart, rangeEnd) {
|
||||
return (1 - frac) * rangeStart + frac * rangeEnd;
|
||||
}
|
||||
|
||||
function scaleRangeWithClamping(
|
||||
val,
|
||||
sourceRangeStart,
|
||||
sourceRangeEnd,
|
||||
destRangeStart,
|
||||
destRangeEnd
|
||||
) {
|
||||
const frac = clamp(
|
||||
(val - sourceRangeStart) / (sourceRangeEnd - sourceRangeStart),
|
||||
0,
|
||||
1
|
||||
);
|
||||
return lerp(frac, destRangeStart, destRangeEnd);
|
||||
}
|
||||
|
||||
function calculateOverhead(state) {
|
||||
const overheadFromSampling =
|
||||
scaleRangeWithClamping(
|
||||
Math.log(state.interval),
|
||||
Math.log(0.05),
|
||||
Math.log(1),
|
||||
1,
|
||||
0
|
||||
) +
|
||||
scaleRangeWithClamping(
|
||||
Math.log(state.interval),
|
||||
Math.log(1),
|
||||
Math.log(100),
|
||||
0.1,
|
||||
0
|
||||
);
|
||||
const overheadFromBuffersize = scaleRangeWithClamping(
|
||||
Math.log(state.buffersize),
|
||||
Math.log(10),
|
||||
Math.log(1000000),
|
||||
0,
|
||||
0.1
|
||||
);
|
||||
const overheadFromStackwalk = state.stackwalk ? 0.05 : 0;
|
||||
const overheadFromResponsiveness = state.responsiveness ? 0.05 : 0;
|
||||
const overheadFromJavaScrpt = state.js ? 0.05 : 0;
|
||||
const overheadFromSeqStyle = state.seqstyle ? 0.05 : 0;
|
||||
const overheadFromTaskTracer = state.tasktracer ? 0.05 : 0;
|
||||
const overheadFromJSTracer = state.jstracer ? 0.05 : 0;
|
||||
return clamp(
|
||||
overheadFromSampling +
|
||||
overheadFromBuffersize +
|
||||
overheadFromStackwalk +
|
||||
overheadFromResponsiveness +
|
||||
overheadFromJavaScrpt +
|
||||
overheadFromSeqStyle +
|
||||
overheadFromTaskTracer +
|
||||
overheadFromJSTracer,
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
function getBackground() {
|
||||
return browser.runtime.getBackgroundPage();
|
||||
}
|
||||
|
||||
document.querySelector(".button-start").addEventListener("click", async () => {
|
||||
const background = await getBackground();
|
||||
await background.startProfiler();
|
||||
background.adjustState({ isRunning: true });
|
||||
renderState(background.profilerState);
|
||||
});
|
||||
|
||||
document.querySelector(".button-cancel").addEventListener("click", async () => {
|
||||
const background = await getBackground();
|
||||
await background.stopProfiler();
|
||||
background.adjustState({ isRunning: false });
|
||||
renderState(background.profilerState);
|
||||
});
|
||||
|
||||
document
|
||||
.querySelector("#button-capture")
|
||||
.addEventListener("click", async () => {
|
||||
if (document.documentElement.classList.contains("status-running")) {
|
||||
const background = await getBackground();
|
||||
await background.captureProfile();
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
|
||||
document
|
||||
.querySelector("#settings-label")
|
||||
.addEventListener("click", async () => {
|
||||
const background = await getBackground();
|
||||
background.adjustState({
|
||||
settingsOpen: !background.profilerState.settingsOpen,
|
||||
});
|
||||
renderState(background.profilerState);
|
||||
});
|
||||
|
||||
document.querySelector(".interval-range").addEventListener("input", async e => {
|
||||
const background = await getBackground();
|
||||
const frac = e.target.value / 100;
|
||||
background.adjustState({
|
||||
interval: intervalScale.fromFractionToSingleDigitValue(frac),
|
||||
});
|
||||
renderState(background.profilerState);
|
||||
});
|
||||
|
||||
document
|
||||
.querySelector(".buffersize-range")
|
||||
.addEventListener("input", async e => {
|
||||
const background = await getBackground();
|
||||
const frac = e.target.value / 100;
|
||||
background.adjustState({
|
||||
buffersize: buffersizeScale.fromFractionToSingleDigitValue(frac),
|
||||
});
|
||||
renderState(background.profilerState);
|
||||
});
|
||||
|
||||
document
|
||||
.querySelector(".windowlength-range")
|
||||
.addEventListener("input", async e => {
|
||||
const background = await getBackground();
|
||||
const frac = e.target.value / 100;
|
||||
background.adjustState({
|
||||
windowLength: windowLengthScale.fromFractionToSingleDigitValue(frac),
|
||||
});
|
||||
renderState(background.profilerState);
|
||||
});
|
||||
|
||||
window.onload = async () => {
|
||||
if (
|
||||
!browser.geckoProfiler.supports ||
|
||||
!browser.geckoProfiler.supports.WINDOWLENGTH
|
||||
) {
|
||||
document.body.classList.add("no-windowlength");
|
||||
}
|
||||
|
||||
// Letting the background script know how the infiniteWindowLength is represented.
|
||||
const background = await getBackground();
|
||||
background.adjustState({
|
||||
infiniteWindowLength,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This helper initializes and adds listeners to the features checkboxes that
|
||||
* will adjust the profiler state when changed.
|
||||
*/
|
||||
async function setupFeatureCheckbox(name) {
|
||||
const platform = await browser.runtime.getPlatformInfo();
|
||||
|
||||
// Java profiling is only meaningful on android.
|
||||
if (name == "java") {
|
||||
if (platform.os !== "android") {
|
||||
document.querySelector("#java").style.display = "none";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const checkbox = document.querySelector(`#${featurePrefix}${name}`);
|
||||
const background = await getBackground();
|
||||
checkbox.checked = background.profilerState.features[name];
|
||||
|
||||
checkbox.addEventListener("change", async e => {
|
||||
const features = Object.assign({}, background.profilerState.features);
|
||||
features[name] = e.target.checked;
|
||||
background.adjustState({ features });
|
||||
renderState(background.profilerState);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper initializes and adds listeners to the threads checkboxes that
|
||||
* will adjust the profiler state when changed.
|
||||
*/
|
||||
async function setupThreadCheckbox(name) {
|
||||
const checkbox = document.querySelector(`#${threadPrefix}${name}`);
|
||||
const background = await getBackground();
|
||||
checkbox.checked = background.profilerState.threads.includes(threadMap[name]);
|
||||
|
||||
checkbox.addEventListener("change", async e => {
|
||||
let threads = background.profilerState.threads;
|
||||
if (e.target.checked) {
|
||||
threads += "," + e.target.value;
|
||||
} else {
|
||||
threads = threadTextToList(threads)
|
||||
.filter(thread => thread !== e.target.value)
|
||||
.join(",");
|
||||
}
|
||||
background.adjustState({ threads });
|
||||
renderState(background.profilerState);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the thread list string into a list of values.
|
||||
* @param string threads, comma separated values.
|
||||
* @return Array list of thread names
|
||||
*/
|
||||
function threadTextToList(threads) {
|
||||
return (
|
||||
threads
|
||||
// Split on commas
|
||||
.split(",")
|
||||
// Clean up any extraneous whitespace
|
||||
.map(string => string.trim())
|
||||
// Filter out any blank strings
|
||||
.filter(string => string)
|
||||
);
|
||||
}
|
||||
|
||||
for (const name of features) {
|
||||
setupFeatureCheckbox(name);
|
||||
}
|
||||
|
||||
for (const name in threadMap) {
|
||||
setupThreadCheckbox(name);
|
||||
}
|
||||
|
||||
document
|
||||
.querySelector("#perf-settings-thread-text")
|
||||
.addEventListener("change", async e => {
|
||||
const background = await getBackground();
|
||||
background.adjustState({ threads: e.target.value });
|
||||
renderState(background.profilerState);
|
||||
});
|
||||
|
||||
document
|
||||
.querySelector(".settings-apply-button")
|
||||
.addEventListener("click", async () => {
|
||||
(await getBackground()).restartProfiler();
|
||||
});
|
||||
|
||||
function makeExponentialScale(rangeStart, rangeEnd) {
|
||||
const startExp = Math.log(rangeStart);
|
||||
const endExp = Math.log(rangeEnd);
|
||||
const fromFractionToValue = frac =>
|
||||
Math.exp((1 - frac) * startExp + frac * endExp);
|
||||
const fromValueToFraction = value =>
|
||||
(Math.log(value) - startExp) / (endExp - startExp);
|
||||
const fromFractionToSingleDigitValue = frac => {
|
||||
return +fromFractionToValue(frac).toPrecision(1);
|
||||
};
|
||||
return {
|
||||
fromFractionToValue,
|
||||
fromValueToFraction,
|
||||
fromFractionToSingleDigitValue,
|
||||
};
|
||||
}
|
||||
|
||||
const prettyBytes = (function(module) {
|
||||
"use strict";
|
||||
const UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
|
||||
module.exports = num => {
|
||||
if (!Number.isFinite(num)) {
|
||||
throw new TypeError(
|
||||
`Expected a finite number, got ${typeof num}: ${num}`
|
||||
);
|
||||
}
|
||||
|
||||
const neg = num < 0;
|
||||
|
||||
if (neg) {
|
||||
num = -num;
|
||||
}
|
||||
|
||||
if (num < 1) {
|
||||
return (neg ? "-" : "") + num + " B";
|
||||
}
|
||||
|
||||
const exponent = Math.min(
|
||||
Math.floor(Math.log(num) / Math.log(1000)),
|
||||
UNITS.length - 1
|
||||
);
|
||||
const numStr = Number((num / Math.pow(1000, exponent)).toPrecision(3));
|
||||
const unit = UNITS[exponent];
|
||||
|
||||
return (neg ? "-" : "") + numStr + " " + unit;
|
||||
};
|
||||
|
||||
return module;
|
||||
})({}).exports;
|
||||
|
||||
getBackground().then(background => renderState(background.profilerState));
|
Loading…
Reference in New Issue
Block a user