зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 2 changesets (bug 1632794) for build bustages on ChromeUtils.cpp . CLOSED TREE
Backed out changeset 846b78435f92 (bug 1632794) Backed out changeset 937d7ef96c5d (bug 1632794)
This commit is contained in:
Родитель
87df0b2d72
Коммит
cdf603f65b
|
@ -101,8 +101,6 @@ static const RedirEntry kRedirMap[] = {
|
||||||
nsIAboutModule::ALLOW_SCRIPT},
|
nsIAboutModule::ALLOW_SCRIPT},
|
||||||
{"plugins", "chrome://global/content/plugins.html",
|
{"plugins", "chrome://global/content/plugins.html",
|
||||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD},
|
nsIAboutModule::URI_MUST_LOAD_IN_CHILD},
|
||||||
{"processes", "chrome://global/content/aboutProcesses.html",
|
|
||||||
nsIAboutModule::ALLOW_SCRIPT},
|
|
||||||
// about:serviceworkers always wants to load in the parent process because
|
// about:serviceworkers always wants to load in the parent process because
|
||||||
// when dom.serviceWorkers.parent_intercept is set to true (the new default)
|
// when dom.serviceWorkers.parent_intercept is set to true (the new default)
|
||||||
// then the only place nsIServiceWorkerManager has any data is in the
|
// then the only place nsIServiceWorkerManager has any data is in the
|
||||||
|
|
|
@ -36,8 +36,6 @@ if defined('MOZ_CRASHREPORTER'):
|
||||||
about_pages.append('crashes')
|
about_pages.append('crashes')
|
||||||
if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] != 'android':
|
if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] != 'android':
|
||||||
about_pages.append('profiles')
|
about_pages.append('profiles')
|
||||||
if defined('NIGHTLY_BUILD'):
|
|
||||||
about_pages.append('processes')
|
|
||||||
|
|
||||||
Headers = ['/docshell/build/nsDocShellModule.h']
|
Headers = ['/docshell/build/nsDocShellModule.h']
|
||||||
|
|
||||||
|
|
|
@ -741,12 +741,9 @@ static WebIDLProcType ProcTypeToWebIDL(mozilla::ProcType aType) {
|
||||||
|
|
||||||
switch (aType) {
|
switch (aType) {
|
||||||
PROCTYPE_TO_WEBIDL_CASE(Web, Web);
|
PROCTYPE_TO_WEBIDL_CASE(Web, Web);
|
||||||
PROCTYPE_TO_WEBIDL_CASE(WebIsolated, WebIsolated);
|
|
||||||
PROCTYPE_TO_WEBIDL_CASE(File, File);
|
PROCTYPE_TO_WEBIDL_CASE(File, File);
|
||||||
PROCTYPE_TO_WEBIDL_CASE(Extension, Extension);
|
PROCTYPE_TO_WEBIDL_CASE(Extension, Extension);
|
||||||
PROCTYPE_TO_WEBIDL_CASE(PrivilegedAbout, Privilegedabout);
|
PROCTYPE_TO_WEBIDL_CASE(PrivilegedAbout, Privilegedabout);
|
||||||
PROCTYPE_TO_WEBIDL_CASE(PrivilegedMozilla, Privilegedmozilla);
|
|
||||||
PROCTYPE_TO_WEBIDL_CASE(WebCOOPCOEP, WithCoopCoep);
|
|
||||||
PROCTYPE_TO_WEBIDL_CASE(WebLargeAllocation, WebLargeAllocation);
|
PROCTYPE_TO_WEBIDL_CASE(WebLargeAllocation, WebLargeAllocation);
|
||||||
PROCTYPE_TO_WEBIDL_CASE(Browser, Browser);
|
PROCTYPE_TO_WEBIDL_CASE(Browser, Browser);
|
||||||
PROCTYPE_TO_WEBIDL_CASE(Plugin, Plugin);
|
PROCTYPE_TO_WEBIDL_CASE(Plugin, Plugin);
|
||||||
|
@ -792,7 +789,7 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
|
||||||
global->EventTargetFor(TaskCategory::Performance);
|
global->EventTargetFor(TaskCategory::Performance);
|
||||||
|
|
||||||
// Getting the parent proc info
|
// Getting the parent proc info
|
||||||
mozilla::GetProcInfo(parentPid, 0, mozilla::ProcType::Browser, NS_LITERAL_STRING(""))
|
mozilla::GetProcInfo(parentPid, 0, mozilla::ProcType::Browser)
|
||||||
->Then(
|
->Then(
|
||||||
target, __func__,
|
target, __func__,
|
||||||
[target, domPromise, parentPid](ProcInfo aParentInfo) {
|
[target, domPromise, parentPid](ProcInfo aParentInfo) {
|
||||||
|
@ -806,7 +803,7 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
|
||||||
if (!aGeckoProcess->GetChildProcessHandle()) {
|
if (!aGeckoProcess->GetChildProcessHandle()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nsAutoString origin;
|
|
||||||
base::ProcessId childPid =
|
base::ProcessId childPid =
|
||||||
base::GetProcId(aGeckoProcess->GetChildProcessHandle());
|
base::GetProcId(aGeckoProcess->GetChildProcessHandle());
|
||||||
int32_t childId = 0;
|
int32_t childId = 0;
|
||||||
|
@ -826,51 +823,22 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
|
||||||
if (!contentParent) {
|
if (!contentParent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Converting the remoteType into a ProcType.
|
// Converting the Content Type into a ProcType
|
||||||
// Ideally, the remoteType should be strongly typed
|
nsAutoString processType;
|
||||||
// upstream, this would make the conversion less brittle.
|
processType.Assign(contentParent->GetRemoteType());
|
||||||
nsAutoString remoteType(contentParent->GetRemoteType());
|
if (IsWebRemoteType(processType)) {
|
||||||
if (StringBeginsWith(
|
|
||||||
remoteType,
|
|
||||||
NS_LITERAL_STRING(FISSION_WEB_REMOTE_TYPE))) {
|
|
||||||
// WARNING: Do not change the order, as
|
|
||||||
// `DEFAULT_REMOTE_TYPE` is a prefix of
|
|
||||||
// `FISSION_WEB_REMOTE_TYPE`.
|
|
||||||
type = mozilla::ProcType::WebIsolated;
|
|
||||||
} else if (StringBeginsWith(
|
|
||||||
remoteType,
|
|
||||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
|
|
||||||
type = mozilla::ProcType::Web;
|
type = mozilla::ProcType::Web;
|
||||||
} else if (remoteType.EqualsLiteral(FILE_REMOTE_TYPE)) {
|
} else if (processType.EqualsLiteral(FILE_REMOTE_TYPE)) {
|
||||||
type = mozilla::ProcType::File;
|
type = mozilla::ProcType::File;
|
||||||
} else if (remoteType.EqualsLiteral(
|
} else if (processType.EqualsLiteral(
|
||||||
EXTENSION_REMOTE_TYPE)) {
|
EXTENSION_REMOTE_TYPE)) {
|
||||||
type = mozilla::ProcType::Extension;
|
type = mozilla::ProcType::Extension;
|
||||||
} else if (remoteType.EqualsLiteral(
|
} else if (processType.EqualsLiteral(
|
||||||
PRIVILEGEDABOUT_REMOTE_TYPE)) {
|
PRIVILEGEDABOUT_REMOTE_TYPE)) {
|
||||||
type = mozilla::ProcType::PrivilegedAbout;
|
type = mozilla::ProcType::PrivilegedAbout;
|
||||||
} else if (remoteType.EqualsLiteral(
|
} else if (processType.EqualsLiteral(
|
||||||
PRIVILEGEDMOZILLA_REMOTE_TYPE)) {
|
|
||||||
type = mozilla::ProcType::PrivilegedMozilla;
|
|
||||||
} else if (StringBeginsWith(
|
|
||||||
remoteType,
|
|
||||||
NS_LITERAL_STRING(
|
|
||||||
WITH_COOP_COEP_REMOTE_TYPE_PREFIX))) {
|
|
||||||
type = mozilla::ProcType::WebCOOPCOEP;
|
|
||||||
} else if (remoteType.EqualsLiteral(
|
|
||||||
LARGE_ALLOCATION_REMOTE_TYPE)) {
|
LARGE_ALLOCATION_REMOTE_TYPE)) {
|
||||||
type = mozilla::ProcType::WebLargeAllocation;
|
type = mozilla::ProcType::WebLargeAllocation;
|
||||||
} else {
|
|
||||||
MOZ_CRASH("Unknown remoteType");
|
|
||||||
}
|
|
||||||
|
|
||||||
// By convention, everything after '=' is the origin.
|
|
||||||
nsAString::const_iterator cursor;
|
|
||||||
nsAString::const_iterator end;
|
|
||||||
remoteType.BeginReading(cursor);
|
|
||||||
remoteType.EndReading(end);
|
|
||||||
if (FindCharInReadable(u'=', cursor, end)) {
|
|
||||||
origin = Substring(++cursor, end);
|
|
||||||
}
|
}
|
||||||
childId = contentParent->ChildID();
|
childId = contentParent->ChildID();
|
||||||
break;
|
break;
|
||||||
|
@ -911,10 +879,10 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
|
||||||
|
|
||||||
promises.AppendElement(
|
promises.AppendElement(
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
mozilla::GetProcInfo(childPid, childId, type, origin,
|
mozilla::GetProcInfo(childPid, childId, type,
|
||||||
aGeckoProcess->GetChildTask())
|
aGeckoProcess->GetChildTask())
|
||||||
#else
|
#else
|
||||||
mozilla::GetProcInfo(childPid, childId, type, origin)
|
mozilla::GetProcInfo(childPid, childId, type)
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -960,7 +928,6 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
|
||||||
// Basic info.
|
// Basic info.
|
||||||
childProcInfo->mChildID = info.childId;
|
childProcInfo->mChildID = info.childId;
|
||||||
childProcInfo->mType = ProcTypeToWebIDL(info.type);
|
childProcInfo->mType = ProcTypeToWebIDL(info.type);
|
||||||
childProcInfo->mOrigin = info.origin;
|
|
||||||
childProcInfo->mPid = info.pid;
|
childProcInfo->mPid = info.pid;
|
||||||
childProcInfo->mFilename.Assign(info.filename);
|
childProcInfo->mFilename.Assign(info.filename);
|
||||||
childProcInfo->mVirtualMemorySize = info.virtualMemorySize;
|
childProcInfo->mVirtualMemorySize = info.virtualMemorySize;
|
||||||
|
|
|
@ -498,13 +498,10 @@ partial namespace ChromeUtils {
|
||||||
*/
|
*/
|
||||||
enum WebIDLProcType {
|
enum WebIDLProcType {
|
||||||
"web",
|
"web",
|
||||||
"webIsolated",
|
|
||||||
"file",
|
"file",
|
||||||
"extension",
|
"extension",
|
||||||
"privilegedabout",
|
"privilegedabout",
|
||||||
"privilegedmozilla",
|
|
||||||
"webLargeAllocation",
|
"webLargeAllocation",
|
||||||
"withCoopCoep",
|
|
||||||
"browser",
|
"browser",
|
||||||
"plugin",
|
"plugin",
|
||||||
"ipdlUnitTest",
|
"ipdlUnitTest",
|
||||||
|
@ -544,7 +541,6 @@ dictionary ChildProcInfoDictionary {
|
||||||
sequence<ThreadInfoDictionary> threads = [];
|
sequence<ThreadInfoDictionary> threads = [];
|
||||||
// Firefox info
|
// Firefox info
|
||||||
unsigned long long ChildID = 0;
|
unsigned long long ChildID = 0;
|
||||||
DOMString origin = "";
|
|
||||||
WebIDLProcType type = "web";
|
WebIDLProcType type = "web";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
@import url("chrome://global/skin/in-content/common.css");
|
|
||||||
|
|
||||||
html {
|
|
||||||
background-color: var(--in-content-page-background);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
#process-table {
|
|
||||||
-moz-user-select: none;
|
|
||||||
font-size: 1em;
|
|
||||||
border-spacing: 0;
|
|
||||||
background-color: var(--in-content-box-background);
|
|
||||||
margin: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
inset-inline-start: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-width: 40em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Avoid scrolling the header */
|
|
||||||
#process-tbody {
|
|
||||||
display: block;
|
|
||||||
margin-top: 2em;
|
|
||||||
}
|
|
||||||
#process-thead {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1;
|
|
||||||
height: 2em;
|
|
||||||
border-bottom: 1px solid var(--in-content-border-color);
|
|
||||||
min-width: 40em;
|
|
||||||
background-color: var(--in-content-box-background);
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
display: table;
|
|
||||||
table-layout: fixed;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
td:nth-child(1) {
|
|
||||||
width: 6%;
|
|
||||||
}
|
|
||||||
/* At least one column needs to have a flexible width,
|
|
||||||
so no width specified for td:nth-child(2) */
|
|
||||||
td:nth-child(3) {
|
|
||||||
width: 10%;
|
|
||||||
}
|
|
||||||
td:nth-child(4) {
|
|
||||||
width: 10%;
|
|
||||||
}
|
|
||||||
td:nth-child(5) {
|
|
||||||
width: 10%;
|
|
||||||
}
|
|
||||||
td:nth-child(6) {
|
|
||||||
width: 10%;
|
|
||||||
}
|
|
||||||
td:nth-child(7) {
|
|
||||||
width: 2%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#process-thead > tr {
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#process-thead > tr > td {
|
|
||||||
border: none;
|
|
||||||
background-color: var(--in-content-button-background);
|
|
||||||
}
|
|
||||||
#process-thead > tr > td:not(:first-child) {
|
|
||||||
border-inline-start-width: 1px;
|
|
||||||
border-inline-start-style: solid;
|
|
||||||
border-image: linear-gradient(transparent 0%, transparent 20%, var(--in-content-box-border-color) 20%, var(--in-content-box-border-color) 80%, transparent 80%, transparent 100%) 1 1;
|
|
||||||
border-bottom: 1px solid var(--in-content-border-color);
|
|
||||||
}
|
|
||||||
td {
|
|
||||||
padding: 5px 10px;
|
|
||||||
min-height: 2em;
|
|
||||||
color: var(--in-content-text-color);
|
|
||||||
max-width: 70vw;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
#process-tbody > tr > td:first-child {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.twisty {
|
|
||||||
margin-inline: -10px 0px;
|
|
||||||
padding-inline: 18px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
/* Putting the background image in a positioned pseudo element lets us
|
|
||||||
* use CSS transforms on the background image, which we need for rtl. */
|
|
||||||
.twisty::before {
|
|
||||||
content: url("chrome://global/skin/icons/twisty-collapsed.svg");
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
line-height: 50%;
|
|
||||||
top: 4px; /* Half the image's height */
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
-moz-context-properties: fill;
|
|
||||||
fill: currentColor;
|
|
||||||
}
|
|
||||||
.twisty:dir(rtl)::before {
|
|
||||||
transform: scaleX(-1);
|
|
||||||
}
|
|
||||||
.twisty.open::before {
|
|
||||||
content: url("chrome://global/skin/icons/twisty-expanded.svg");
|
|
||||||
}
|
|
||||||
#process-tbody > tr > td.indent {
|
|
||||||
padding-inline: 36px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#process-tbody > tr[selected] > td {
|
|
||||||
background-color: var(--in-content-item-selected);
|
|
||||||
color: var(--in-content-selected-text);
|
|
||||||
}
|
|
||||||
#process-tbody > tr:hover {
|
|
||||||
background-color: var(--in-content-item-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clickable {
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: right 4px center;
|
|
||||||
}
|
|
||||||
.clickable:dir(rtl) {
|
|
||||||
background-position-x: left 4px;
|
|
||||||
}
|
|
||||||
.asc {
|
|
||||||
background-image: url(chrome://global/skin/icons/arrow-up-12.svg);
|
|
||||||
-moz-context-properties: fill;
|
|
||||||
fill: currentColor;
|
|
||||||
}
|
|
||||||
.desc {
|
|
||||||
background-image: url(chrome://global/skin/icons/arrow-dropdown-12.svg);
|
|
||||||
-moz-context-properties: fill;
|
|
||||||
fill: currentColor;
|
|
||||||
}
|
|
||||||
#process-thead > tr > td.clickable:hover {
|
|
||||||
background-color: var(--in-content-button-background-hover);
|
|
||||||
}
|
|
||||||
#process-thead > tr > td.clickable:active {
|
|
||||||
background-color: var(--in-content-button-background-active);
|
|
||||||
}
|
|
||||||
|
|
||||||
#process-tbody > tr.process {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
#process-tbody > tr.thread {
|
|
||||||
font-size-adjust: 0.5;
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
# 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/.
|
|
||||||
|
|
||||||
# Page title
|
|
||||||
about-processes-title = Process Manager
|
|
||||||
|
|
||||||
## Column headers
|
|
||||||
|
|
||||||
about-processes-column-id = Id
|
|
||||||
about-processes-column-type = Type
|
|
||||||
about-processes-column-name = Name
|
|
||||||
about-processes-column-memory-resident = Memory (Resident)
|
|
||||||
about-processes-column-memory-virtual = Memory (Virtual)
|
|
||||||
about-processes-column-cpu-user = CPU (User)
|
|
||||||
about-processes-column-cpu-kernel = CPU (Kernel)
|
|
||||||
about-processes-column-cpu-threads = CPU (Threads)
|
|
||||||
about-processes-column-threads = Threads
|
|
|
@ -1,31 +0,0 @@
|
||||||
<!-- 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/. -->
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src chrome:;img-src data:; object-src 'none'">
|
|
||||||
<title data-l10n-id="about-processes-title"></title>
|
|
||||||
<link rel="icon" id="favicon" href="chrome://global/skin/icons/performance.svg">
|
|
||||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
|
||||||
<link rel="localization" href="preview/aboutProcesses.ftl">
|
|
||||||
<script src="chrome://global/content/aboutProcesses.js"></script>
|
|
||||||
<link rel="stylesheet" href="chrome://global/content/aboutProcesses.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<table id="process-table">
|
|
||||||
<thead id="process-thead">
|
|
||||||
<tr>
|
|
||||||
<td class="clickable" id="column-pid" data-l10n-id="about-processes-column-id"></td>
|
|
||||||
<td class="clickable" id="column-name" data-l10n-id="about-processes-column-name"></td>
|
|
||||||
<td class="clickable" id="column-memory-resident" data-l10n-id="about-processes-column-memory-resident"></td> <!-- Memory usage. -->
|
|
||||||
<td class="clickable" id="column-memory-virtual" data-l10n-id="about-processes-column-memory-virtual"></td> <!-- Memory usage. -->
|
|
||||||
<td class="clickable" id="column-cpu-user" data-l10n-id="about-processes-column-cpu-user"></td> <!-- CPU user. -->
|
|
||||||
<td class="clickable" id="column-cpu-kernel" data-l10n-id="about-processes-column-cpu-kernel"></td> <!-- CPU kernel. -->
|
|
||||||
<td class="clickable" id="column-cpu-threads" data-l10n-id="about-processes-column-threads"></td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="process-tbody"></tbody>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,790 +0,0 @@
|
||||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/
|
|
||||||
/* vim: set ts=8 sts=2 et sw=2 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// Time in ms before we start changing the sort order again after receiving a
|
|
||||||
// mousemove event.
|
|
||||||
const TIME_BEFORE_SORTING_AGAIN = 5000;
|
|
||||||
|
|
||||||
// How often we should add a sample to our buffer.
|
|
||||||
const BUFFER_SAMPLING_RATE_MS = 1000;
|
|
||||||
|
|
||||||
// The age of the oldest sample to keep.
|
|
||||||
const BUFFER_DURATION_MS = 10000;
|
|
||||||
|
|
||||||
// How often we should update
|
|
||||||
const UPDATE_INTERVAL_MS = 2000;
|
|
||||||
|
|
||||||
const MS_PER_NS = 1000000;
|
|
||||||
const NS_PER_S = 1000000000;
|
|
||||||
|
|
||||||
const ONE_GIGA = 1024 * 1024 * 1024;
|
|
||||||
const ONE_MEGA = 1024 * 1024;
|
|
||||||
const ONE_KILO = 1024;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a Promise that's resolved after the next turn of the event loop.
|
|
||||||
*
|
|
||||||
* Just returning a resolved Promise would mean that any `then` callbacks
|
|
||||||
* would be called right after the end of the current turn, so `setTimeout`
|
|
||||||
* is used to delay Promise resolution until the next turn.
|
|
||||||
*
|
|
||||||
* In mochi tests, it's possible for this to be called after the
|
|
||||||
* about:performance window has been torn down, which causes `setTimeout` to
|
|
||||||
* throw an NS_ERROR_NOT_INITIALIZED exception. In that case, returning
|
|
||||||
* `undefined` is fine.
|
|
||||||
*/
|
|
||||||
function wait(ms = 0) {
|
|
||||||
try {
|
|
||||||
let resolve;
|
|
||||||
let p = new Promise(resolve_ => {
|
|
||||||
resolve = resolve_;
|
|
||||||
});
|
|
||||||
setTimeout(resolve, ms);
|
|
||||||
return p;
|
|
||||||
} catch (e) {
|
|
||||||
dump(
|
|
||||||
"WARNING: wait aborted because of an invalid Window state in aboutPerformance.js.\n"
|
|
||||||
);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utilities for dealing with state
|
|
||||||
*/
|
|
||||||
var State = {
|
|
||||||
/**
|
|
||||||
* Indexed by the number of minutes since the snapshot was taken.
|
|
||||||
*
|
|
||||||
* @type {Array<ApplicationSnapshot>}
|
|
||||||
*/
|
|
||||||
_buffer: [],
|
|
||||||
/**
|
|
||||||
* The latest snapshot.
|
|
||||||
*
|
|
||||||
* @type ApplicationSnapshot
|
|
||||||
*/
|
|
||||||
_latest: null,
|
|
||||||
|
|
||||||
async _promiseSnapshot() {
|
|
||||||
let main = await ChromeUtils.requestProcInfo();
|
|
||||||
|
|
||||||
let processes = new Map();
|
|
||||||
processes.set(main.pid, main);
|
|
||||||
for (let child of main.children) {
|
|
||||||
processes.set(child.pid, child);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { processes, date: Cu.now() };
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the internal state.
|
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
async update() {
|
|
||||||
// If the buffer is empty, add one value for bootstraping purposes.
|
|
||||||
if (!this._buffer.length) {
|
|
||||||
this._latest = await this._promiseSnapshot();
|
|
||||||
this._buffer.push(this._latest);
|
|
||||||
await wait(BUFFER_SAMPLING_RATE_MS * 1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let now = Cu.now();
|
|
||||||
|
|
||||||
// If we haven't sampled in a while, add a sample to the buffer.
|
|
||||||
let latestInBuffer = this._buffer[this._buffer.length - 1];
|
|
||||||
let deltaT = now - latestInBuffer.date;
|
|
||||||
if (deltaT > BUFFER_SAMPLING_RATE_MS) {
|
|
||||||
this._latest = await this._promiseSnapshot();
|
|
||||||
this._buffer.push(this._latest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have too many samples, remove the oldest sample.
|
|
||||||
let oldestInBuffer = this._buffer[0];
|
|
||||||
if (oldestInBuffer.date + BUFFER_DURATION_MS < this._latest.date) {
|
|
||||||
this._buffer.shift();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_getThreadDelta(cur, prev, deltaT) {
|
|
||||||
let name = cur.name || "???";
|
|
||||||
let result = {
|
|
||||||
tid: cur.tid,
|
|
||||||
name,
|
|
||||||
// Total amount of CPU used, in ns (user).
|
|
||||||
totalCpuUser: cur.cpuUser,
|
|
||||||
slopeCpuUser: null,
|
|
||||||
// Total amount of CPU used, in ns (kernel).
|
|
||||||
totalCpuKernel: cur.cpuKernel,
|
|
||||||
slopeCpuKernel: null,
|
|
||||||
};
|
|
||||||
if (!prev) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (prev.tid != cur.tid) {
|
|
||||||
throw new Error("Assertion failed: A thread cannot change tid.");
|
|
||||||
}
|
|
||||||
result.slopeCpuUser = (cur.cpuUser - prev.cpuUser) / deltaT;
|
|
||||||
result.slopeCpuKernel = (cur.cpuKernel - prev.cpuKernel) / deltaT;
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the delta between two process snapshots.
|
|
||||||
*
|
|
||||||
* @param {ProcessSnapshot} cur
|
|
||||||
* @param {ProcessSnapshot?} prev
|
|
||||||
* @param {Number?} deltaT A number of nanoseconds elapsed between `cur` and `prev`.
|
|
||||||
*/
|
|
||||||
_getProcessDelta(cur, prev, deltaT) {
|
|
||||||
let result = {
|
|
||||||
pid: cur.pid,
|
|
||||||
filename: cur.filename,
|
|
||||||
totalVirtualMemorySize: cur.virtualMemorySize,
|
|
||||||
deltaVirtualMemorySize: null,
|
|
||||||
totalResidentSize: cur.residentSetSize,
|
|
||||||
deltaResidentSize: null,
|
|
||||||
totalCpuUser: cur.cpuUser,
|
|
||||||
slopeCpuUser: null,
|
|
||||||
totalCpuKernel: cur.cpuKernel,
|
|
||||||
slopeCpuKernel: null,
|
|
||||||
type: cur.type,
|
|
||||||
origin: cur.origin || "",
|
|
||||||
threads: null,
|
|
||||||
};
|
|
||||||
if (!prev) {
|
|
||||||
result.threads = cur.threads.map(data =>
|
|
||||||
this._getThreadDelta(data, null, null)
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (prev.pid != cur.pid) {
|
|
||||||
throw new Error("Assertion failed: A process cannot change pid.");
|
|
||||||
}
|
|
||||||
if (prev.type != cur.type) {
|
|
||||||
throw new Error("Assertion failed: A process cannot change type.");
|
|
||||||
}
|
|
||||||
let prevThreads = new Map();
|
|
||||||
for (let thread of prev.threads) {
|
|
||||||
prevThreads.set(thread.tid, thread);
|
|
||||||
}
|
|
||||||
let threads = cur.threads.map(curThread => {
|
|
||||||
let prevThread = prevThreads.get(curThread.tid);
|
|
||||||
if (!prevThread) {
|
|
||||||
return this._getThreadDelta(curThread);
|
|
||||||
}
|
|
||||||
return this._getThreadDelta(curThread, prevThread, deltaT);
|
|
||||||
});
|
|
||||||
result.deltaVirtualMemorySize =
|
|
||||||
cur.virtualMemorySize - prev.virtualMemorySize;
|
|
||||||
result.deltaResidentSize = cur.residentSetSize - prev.residentSetSize;
|
|
||||||
result.slopeCpuUser = (cur.cpuUser - prev.cpuUser) / deltaT;
|
|
||||||
result.slopeCpuKernel = (cur.cpuKernel - prev.cpuKernel) / deltaT;
|
|
||||||
result.threads = threads;
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
getCounters() {
|
|
||||||
// We rebuild the maps during each iteration to make sure that
|
|
||||||
// we do not maintain references to processes that have been
|
|
||||||
// shutdown.
|
|
||||||
|
|
||||||
let previous = this._buffer[Math.max(this._buffer.length - 2, 0)];
|
|
||||||
let current = this._latest;
|
|
||||||
let counters = [];
|
|
||||||
|
|
||||||
for (let cur of current.processes.values()) {
|
|
||||||
// Look for the oldest point of comparison
|
|
||||||
let oldest = null;
|
|
||||||
let delta;
|
|
||||||
for (let index = 0; index <= this._buffer.length - 2; ++index) {
|
|
||||||
oldest = this._buffer[index].processes.get(cur.pid);
|
|
||||||
if (oldest) {
|
|
||||||
// Found it!
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldest) {
|
|
||||||
// Existing process. Let's display slopes info.
|
|
||||||
delta = this._getProcessDelta(
|
|
||||||
cur,
|
|
||||||
oldest,
|
|
||||||
(current.date - previous.date) * MS_PER_NS
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// New process. Let's display basic info.
|
|
||||||
delta = this._getProcessDelta(cur, null, null);
|
|
||||||
}
|
|
||||||
counters.push(delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
return counters;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var View = {
|
|
||||||
_fragment: document.createDocumentFragment(),
|
|
||||||
async commit() {
|
|
||||||
let tbody = document.getElementById("process-tbody");
|
|
||||||
|
|
||||||
// Force translation to happen before we insert the new content in the DOM
|
|
||||||
// to avoid flicker when resizing.
|
|
||||||
await document.l10n.translateFragment(this._fragment);
|
|
||||||
|
|
||||||
while (tbody.firstChild) {
|
|
||||||
tbody.firstChild.remove();
|
|
||||||
}
|
|
||||||
tbody.appendChild(this._fragment);
|
|
||||||
this._fragment = document.createDocumentFragment();
|
|
||||||
},
|
|
||||||
insertAfterRow(row) {
|
|
||||||
row.parentNode.insertBefore(this._fragment, row.nextSibling);
|
|
||||||
this._fragment = document.createDocumentFragment();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a row showing a single process (without its threads).
|
|
||||||
*
|
|
||||||
* @param {ProcessDelta} data The data to display.
|
|
||||||
* @param {bool} isOpen `true` if we're also displaying the threads of this process, `false` otherwise.
|
|
||||||
* @return {DOMElement} The row displaying the process.
|
|
||||||
*/
|
|
||||||
appendProcessRow(data, isOpen) {
|
|
||||||
let row = document.createElement("tr");
|
|
||||||
row.classList.add("process");
|
|
||||||
|
|
||||||
// Column: pid / twisty image
|
|
||||||
{
|
|
||||||
let elt = this._addCell(row, {
|
|
||||||
content: data.pid,
|
|
||||||
classes: ["pid", "root"],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data.threads.length) {
|
|
||||||
let img = document.createElement("span");
|
|
||||||
img.classList.add("twisty", "process");
|
|
||||||
if (isOpen) {
|
|
||||||
img.classList.add("open");
|
|
||||||
}
|
|
||||||
elt.insertBefore(img, elt.firstChild);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column: type
|
|
||||||
{
|
|
||||||
let content = data.origin ? `${data.origin} (${data.type})` : data.type;
|
|
||||||
this._addCell(row, {
|
|
||||||
content,
|
|
||||||
classes: ["type"],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column: Resident size
|
|
||||||
{
|
|
||||||
let { formatedDelta, formatedValue } = this._formatMemoryAndDelta(
|
|
||||||
data.totalResidentSize,
|
|
||||||
data.deltaResidentSize
|
|
||||||
);
|
|
||||||
let content = formatedDelta
|
|
||||||
? `${formatedValue}${formatedDelta}`
|
|
||||||
: formatedValue;
|
|
||||||
this._addCell(row, {
|
|
||||||
content,
|
|
||||||
classes: ["totalResidentSize"],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column: VM size
|
|
||||||
{
|
|
||||||
let { formatedDelta, formatedValue } = this._formatMemoryAndDelta(
|
|
||||||
data.totalVirtualMemorySize,
|
|
||||||
data.deltaVirtualMemorySize
|
|
||||||
);
|
|
||||||
let content = formatedDelta
|
|
||||||
? `${formatedValue}${formatedDelta}`
|
|
||||||
: formatedValue;
|
|
||||||
this._addCell(row, {
|
|
||||||
content,
|
|
||||||
classes: ["totalVirtualMemorySize"],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column: CPU: User
|
|
||||||
{
|
|
||||||
let slope = this._formatPercentage(data.slopeCpuUser);
|
|
||||||
let content = `${slope} (${(
|
|
||||||
data.totalCpuUser / MS_PER_NS
|
|
||||||
).toLocaleString(undefined, { maximumFractionDigits: 0 })}ms)`;
|
|
||||||
this._addCell(row, {
|
|
||||||
content,
|
|
||||||
classes: ["cpuUser"],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column: CPU: Kernel
|
|
||||||
{
|
|
||||||
let slope = this._formatPercentage(data.slopeCpuKernel);
|
|
||||||
let content = `${slope} (${(
|
|
||||||
data.totalCpuKernel / MS_PER_NS
|
|
||||||
).toLocaleString(undefined, { maximumFractionDigits: 0 })}ms)`;
|
|
||||||
this._addCell(row, {
|
|
||||||
content,
|
|
||||||
classes: ["cpuKernel"],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column: Number of threads
|
|
||||||
this._addCell(row, {
|
|
||||||
content: data.threads.length,
|
|
||||||
classes: ["numberOfThreads"],
|
|
||||||
});
|
|
||||||
|
|
||||||
this._fragment.appendChild(row);
|
|
||||||
return row;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a row showing a single thread.
|
|
||||||
*
|
|
||||||
* @param {ThreadDelta} data The data to display.
|
|
||||||
* @return {DOMElement} The row displaying the thread.
|
|
||||||
*/
|
|
||||||
appendThreadRow(data) {
|
|
||||||
let row = document.createElement("tr");
|
|
||||||
row.classList.add("thread");
|
|
||||||
|
|
||||||
// Column: id
|
|
||||||
this._addCell(row, {
|
|
||||||
content: data.tid,
|
|
||||||
classes: ["tid", "indent"],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Column: filename
|
|
||||||
this._addCell(row, {
|
|
||||||
content: data.name,
|
|
||||||
classes: ["name"],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Column: Resident size (empty)
|
|
||||||
this._addCell(row, {
|
|
||||||
content: "",
|
|
||||||
classes: ["totalResidentSize"],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Column: VM size (empty)
|
|
||||||
this._addCell(row, {
|
|
||||||
content: "",
|
|
||||||
classes: ["totalVirtualMemorySize"],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Column: CPU: User
|
|
||||||
{
|
|
||||||
let slope = this._formatPercentage(data.slopeCpuUser);
|
|
||||||
let text = `${slope} (${(
|
|
||||||
data.totalCpuUser / MS_PER_NS
|
|
||||||
).toLocaleString(undefined, { maximumFractionDigits: 0 })} ms)`;
|
|
||||||
this._addCell(row, {
|
|
||||||
content: text,
|
|
||||||
classes: ["cpuUser"],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column: CPU: Kernel
|
|
||||||
{
|
|
||||||
let slope = this._formatPercentage(data.slopeCpuKernel);
|
|
||||||
let text = `${slope} (${(
|
|
||||||
data.totalCpuKernel / MS_PER_NS
|
|
||||||
).toLocaleString(undefined, { maximumFractionDigits: 0 })} ms)`;
|
|
||||||
this._addCell(row, {
|
|
||||||
content: text,
|
|
||||||
classes: ["cpuKernel"],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column: Number of threads (empty)
|
|
||||||
this._addCell(row, {
|
|
||||||
content: "",
|
|
||||||
classes: ["numberOfThreads"],
|
|
||||||
});
|
|
||||||
|
|
||||||
this._fragment.appendChild(row);
|
|
||||||
return row;
|
|
||||||
},
|
|
||||||
|
|
||||||
_addCell(row, { content, classes }) {
|
|
||||||
let elt = document.createElement("td");
|
|
||||||
this._setTextAndTooltip(elt, content);
|
|
||||||
elt.classList.add(...classes);
|
|
||||||
row.appendChild(elt);
|
|
||||||
return elt;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility method to format an optional percentage.
|
|
||||||
*
|
|
||||||
* As a special case, we also handle `null`, which represents the case in which we do
|
|
||||||
* not have sufficient information to compute a percentage.
|
|
||||||
*
|
|
||||||
* @param {Number?} value The value to format. Must be either `null` or a non-negative number.
|
|
||||||
* A value of 1 means 100%. A value larger than 1 is possible as processes can use several
|
|
||||||
* cores.
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
_formatPercentage(value) {
|
|
||||||
if (value == null) {
|
|
||||||
return "?";
|
|
||||||
}
|
|
||||||
if (value < 0 || typeof value != "number") {
|
|
||||||
throw new Error(`Invalid percentage value ${value}`);
|
|
||||||
}
|
|
||||||
if (value == 0) {
|
|
||||||
// Let's make sure that we do not confuse idle and "close to 0%",
|
|
||||||
// otherwise this results in weird displays.
|
|
||||||
return "idle";
|
|
||||||
}
|
|
||||||
// Now work with actual percentages.
|
|
||||||
let percentage = value * 100;
|
|
||||||
if (percentage < 0.01) {
|
|
||||||
// Tiny percentage, let's display something more useful than "0".
|
|
||||||
return "~0%";
|
|
||||||
}
|
|
||||||
if (percentage < 1) {
|
|
||||||
// Still a small percentage, but it should fit within 2 digits.
|
|
||||||
return `${percentage.toLocaleString(undefined, {
|
|
||||||
maximumFractionDigits: 2,
|
|
||||||
})}%`;
|
|
||||||
}
|
|
||||||
// For other percentages, just return a round number.
|
|
||||||
return `${Math.round(percentage)}%`;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format a value representing an amount of memory.
|
|
||||||
*
|
|
||||||
* As a special case, we also handle `null`, which represents the case in which we do
|
|
||||||
* not have sufficient information to compute an amount of memory.
|
|
||||||
*
|
|
||||||
* @param {Number?} value The value to format. Must be either `null` or a non-negative number.
|
|
||||||
* @return { {unit: "GB" | "MB" | "KB" | B" | "?"}, amount: Number } The formated amount and its
|
|
||||||
* unit, which may be used for e.g. additional CSS formating.
|
|
||||||
*/
|
|
||||||
_formatMemory(value) {
|
|
||||||
if (value == null) {
|
|
||||||
return { unit: "?", amount: 0 };
|
|
||||||
}
|
|
||||||
if (value < 0 || typeof value != "number") {
|
|
||||||
throw new Error(`Invalid memory value ${value}`);
|
|
||||||
}
|
|
||||||
if (value >= ONE_GIGA) {
|
|
||||||
return {
|
|
||||||
unit: "GB",
|
|
||||||
amount: Math.ceil((value / ONE_GIGA) * 100) / 100,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (value >= ONE_MEGA) {
|
|
||||||
return {
|
|
||||||
unit: "MB",
|
|
||||||
amount: Math.ceil((value / ONE_MEGA) * 100) / 100,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (value >= ONE_KILO) {
|
|
||||||
return {
|
|
||||||
unit: "KB",
|
|
||||||
amount: Math.ceil((value / ONE_KILO) * 100) / 100,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
unit: "B",
|
|
||||||
amount: Math.round(value),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format a value representing an amount of memory and a delta.
|
|
||||||
*
|
|
||||||
* @param {Number?} value The value to format. Must be either `null` or a non-negative number.
|
|
||||||
* @param {Number?} value The delta to format. Must be either `null` or a non-negative number.
|
|
||||||
* @return {
|
|
||||||
* {unitValue: "GB" | "MB" | "KB" | B" | "?"},
|
|
||||||
* formatedValue: string,
|
|
||||||
* {unitDelta: "GB" | "MB" | "KB" | B" | "?"},
|
|
||||||
* formatedDelta: string
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
_formatMemoryAndDelta(value, delta) {
|
|
||||||
let formatedDelta;
|
|
||||||
let unitDelta;
|
|
||||||
if (delta == null) {
|
|
||||||
formatedDelta == "";
|
|
||||||
unitDelta = null;
|
|
||||||
} else if (delta == 0) {
|
|
||||||
formatedDelta = null;
|
|
||||||
unitDelta = null;
|
|
||||||
} else if (delta >= 0) {
|
|
||||||
let { unit, amount } = this._formatMemory(delta);
|
|
||||||
formatedDelta = ` (+${amount}${unit})`;
|
|
||||||
unitDelta = unit;
|
|
||||||
} else {
|
|
||||||
let { unit, amount } = this._formatMemory(-delta);
|
|
||||||
formatedDelta = ` (-${amount}${unit})`;
|
|
||||||
unitDelta = unit;
|
|
||||||
}
|
|
||||||
let { unit: unitValue, amount } = this._formatMemory(value);
|
|
||||||
return {
|
|
||||||
unitValue,
|
|
||||||
unitDelta,
|
|
||||||
formatedDelta,
|
|
||||||
formatedValue: `${amount}${unitValue}`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
_setTextAndTooltip(elt, text, tooltip = text) {
|
|
||||||
elt.textContent = text;
|
|
||||||
elt.setAttribute("title", tooltip);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var Control = {
|
|
||||||
_openItems: new Set(),
|
|
||||||
_sortColumn: null,
|
|
||||||
_sortAscendent: true,
|
|
||||||
_removeSubtree(row) {
|
|
||||||
while (row.nextSibling && row.nextSibling.classList.contains("thread")) {
|
|
||||||
row.nextSibling.remove();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
init() {
|
|
||||||
let tbody = document.getElementById("process-tbody");
|
|
||||||
tbody.addEventListener("click", event => {
|
|
||||||
this._updateLastMouseEvent();
|
|
||||||
|
|
||||||
// Handle showing or hiding subitems of a row.
|
|
||||||
let target = event.target;
|
|
||||||
if (target.classList.contains("twisty")) {
|
|
||||||
let row = target.parentNode.parentNode;
|
|
||||||
let id = row.process.pid;
|
|
||||||
if (target.classList.toggle("open")) {
|
|
||||||
this._openItems.add(id);
|
|
||||||
this._showChildren(row);
|
|
||||||
View.insertAfterRow(row);
|
|
||||||
} else {
|
|
||||||
this._openItems.delete(id);
|
|
||||||
this._removeSubtree(row);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle selection changes
|
|
||||||
let row = target.parentNode;
|
|
||||||
if (this.selectedRow) {
|
|
||||||
this.selectedRow.removeAttribute("selected");
|
|
||||||
}
|
|
||||||
if (row.windowId) {
|
|
||||||
row.setAttribute("selected", "true");
|
|
||||||
this.selectedRow = row;
|
|
||||||
} else if (this.selectedRow) {
|
|
||||||
this.selectedRow = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tbody.addEventListener("mousemove", () => {
|
|
||||||
this._updateLastMouseEvent();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("visibilitychange", event => {
|
|
||||||
if (!document.hidden) {
|
|
||||||
this._updateDisplay(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document
|
|
||||||
.getElementById("process-thead")
|
|
||||||
.addEventListener("click", async event => {
|
|
||||||
if (!event.target.classList.contains("clickable")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._sortColumn) {
|
|
||||||
const td = document.getElementById(this._sortColumn);
|
|
||||||
td.classList.remove("asc");
|
|
||||||
td.classList.remove("desc");
|
|
||||||
}
|
|
||||||
|
|
||||||
const columnId = event.target.id;
|
|
||||||
if (columnId == this._sortColumn) {
|
|
||||||
// Reverse sorting order.
|
|
||||||
this._sortAscendent = !this._sortAscendent;
|
|
||||||
} else {
|
|
||||||
this._sortColumn = columnId;
|
|
||||||
this._sortAscendent = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._sortAscendent) {
|
|
||||||
event.target.classList.remove("desc");
|
|
||||||
event.target.classList.add("asc");
|
|
||||||
} else {
|
|
||||||
event.target.classList.remove("asc");
|
|
||||||
event.target.classList.add("desc");
|
|
||||||
}
|
|
||||||
|
|
||||||
await this._updateDisplay(true);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_lastMouseEvent: 0,
|
|
||||||
_updateLastMouseEvent() {
|
|
||||||
this._lastMouseEvent = Date.now();
|
|
||||||
},
|
|
||||||
async update() {
|
|
||||||
await State.update();
|
|
||||||
|
|
||||||
if (document.hidden) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await wait(0);
|
|
||||||
|
|
||||||
await this._updateDisplay();
|
|
||||||
},
|
|
||||||
|
|
||||||
// The force parameter can force a full update even when the mouse has been
|
|
||||||
// moved recently.
|
|
||||||
async _updateDisplay(force = false) {
|
|
||||||
if (
|
|
||||||
!force &&
|
|
||||||
Date.now() - this._lastMouseEvent < TIME_BEFORE_SORTING_AGAIN
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let counters = State.getCounters();
|
|
||||||
|
|
||||||
// Reset the selectedRow field and the _openItems set each time we redraw
|
|
||||||
// to avoid keeping forever references to dead processes.
|
|
||||||
let openItems = this._openItems;
|
|
||||||
this._openItems = new Set();
|
|
||||||
|
|
||||||
counters = this._sortProcesses(counters);
|
|
||||||
for (let process of counters) {
|
|
||||||
let isOpen = openItems.has(process.pid);
|
|
||||||
let row = View.appendProcessRow(process, isOpen);
|
|
||||||
row.process = process;
|
|
||||||
if (isOpen) {
|
|
||||||
this._openItems.add(process.pid);
|
|
||||||
this._showChildren(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await View.commit();
|
|
||||||
},
|
|
||||||
_showChildren(row) {
|
|
||||||
let process = row.process;
|
|
||||||
this._sortThreads(process.threads);
|
|
||||||
for (let thread of process.threads) {
|
|
||||||
View.appendThreadRow(thread);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_sortThreads(threads) {
|
|
||||||
return threads.sort((a, b) => {
|
|
||||||
let order;
|
|
||||||
switch (this._sortColumn) {
|
|
||||||
case "column-name":
|
|
||||||
order = a.name.localeCompare(b.name);
|
|
||||||
break;
|
|
||||||
case "column-cpu-user":
|
|
||||||
order = b.slopeCpuUser - a.slopeCpuUser;
|
|
||||||
if (order == 0) {
|
|
||||||
order = b.totalCpuUser - a.totalCpuUser;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "column-cpu-kernel":
|
|
||||||
order = b.slopeCpuKernel - a.slopeCpuKernel;
|
|
||||||
if (order == 0) {
|
|
||||||
order = b.totalCpuKernel - a.totalCpuKernel;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "column-cpu-threads":
|
|
||||||
case "column-memory-resident":
|
|
||||||
case "column-memory-virtual":
|
|
||||||
case "column-type":
|
|
||||||
case "column-pid":
|
|
||||||
case null:
|
|
||||||
order = b.tid - a.tid;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("Unsupported order: " + this._sortColumn);
|
|
||||||
}
|
|
||||||
if (!this._sortAscendent) {
|
|
||||||
order = -order;
|
|
||||||
}
|
|
||||||
return order;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_sortProcesses(counters) {
|
|
||||||
return counters.sort((a, b) => {
|
|
||||||
let order;
|
|
||||||
switch (this._sortColumn) {
|
|
||||||
case "column-pid":
|
|
||||||
order = b.pid - a.pid;
|
|
||||||
break;
|
|
||||||
case "column-type":
|
|
||||||
order = String(a.origin).localeCompare(b.origin);
|
|
||||||
if (order == 0) {
|
|
||||||
order = String(a.type).localeCompare(b.type);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "column-name":
|
|
||||||
order = String(a.name).localeCompare(b.name);
|
|
||||||
break;
|
|
||||||
case "column-cpu-user":
|
|
||||||
order = b.slopeCpuUser - a.slopeCpuUser;
|
|
||||||
if (order == 0) {
|
|
||||||
order = b.totalCpuUser - a.totalCpuUser;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "column-cpu-kernel":
|
|
||||||
order = b.slopeCpuKernel - a.slopeCpuKernel;
|
|
||||||
if (order == 0) {
|
|
||||||
order = b.totalCpuKernel - a.totalCpuKernel;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "column-cpu-threads":
|
|
||||||
order = b.threads.length - a.threads.length;
|
|
||||||
break;
|
|
||||||
case "column-memory-resident":
|
|
||||||
order = b.totalResidentSize - a.totalResidentSize;
|
|
||||||
break;
|
|
||||||
case "column-memory-virtual":
|
|
||||||
order = b.totalVirtualMemorySize - a.totalVirtualMemorySize;
|
|
||||||
break;
|
|
||||||
case null:
|
|
||||||
// Default order: browser goes first.
|
|
||||||
if (a.type == "browser") {
|
|
||||||
order = -1;
|
|
||||||
} else if (b.type == "browser") {
|
|
||||||
order = 1;
|
|
||||||
}
|
|
||||||
// Other processes by increasing pid, arbitrarily.
|
|
||||||
order = b.pid - a.pid;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("Unsupported order: " + this._sortColumn);
|
|
||||||
}
|
|
||||||
if (!this._sortAscendent) {
|
|
||||||
order = -order;
|
|
||||||
}
|
|
||||||
return order;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onload = async function() {
|
|
||||||
Control.init();
|
|
||||||
await Control.update();
|
|
||||||
window.setInterval(() => Control.update(), UPDATE_INTERVAL_MS);
|
|
||||||
};
|
|
|
@ -1,9 +0,0 @@
|
||||||
# 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/.
|
|
||||||
|
|
||||||
toolkit.jar:
|
|
||||||
content/global/aboutProcesses.html (content/aboutProcesses.html)
|
|
||||||
content/global/aboutProcesses.js (content/aboutProcesses.js)
|
|
||||||
content/global/aboutProcesses.css (content/aboutProcesses.css)
|
|
||||||
preview/aboutProcesses.ftl (content/aboutProcesses.ftl)
|
|
|
@ -1,10 +0,0 @@
|
||||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
|
||||||
# vim: set filetype=python:
|
|
||||||
# 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/.
|
|
||||||
|
|
||||||
with Files('**'):
|
|
||||||
BUG_COMPONENT = ('Toolkit', 'Performance Monitoring')
|
|
||||||
|
|
||||||
JAR_MANIFESTS += ['jar.mn']
|
|
|
@ -126,7 +126,3 @@ if CONFIG['MOZ_BUILD_APP'] == 'browser':
|
||||||
# This is only packaged for browser since corrupt JAR and XPI files tend to be a desktop-OS problem.
|
# This is only packaged for browser since corrupt JAR and XPI files tend to be a desktop-OS problem.
|
||||||
if CONFIG['MOZ_BUILD_APP'] == 'browser':
|
if CONFIG['MOZ_BUILD_APP'] == 'browser':
|
||||||
DIRS += ['corroborator']
|
DIRS += ['corroborator']
|
||||||
|
|
||||||
# about:processes is experimental
|
|
||||||
if CONFIG['NIGHTLY_BUILD']:
|
|
||||||
DIRS += ['aboutprocesses']
|
|
||||||
|
|
|
@ -22,13 +22,10 @@ class GeckoChildProcessHost;
|
||||||
enum class ProcType {
|
enum class ProcType {
|
||||||
// These must match the ones in ContentParent.h, and E10SUtils.jsm
|
// These must match the ones in ContentParent.h, and E10SUtils.jsm
|
||||||
Web,
|
Web,
|
||||||
WebIsolated,
|
|
||||||
File,
|
File,
|
||||||
Extension,
|
Extension,
|
||||||
PrivilegedAbout,
|
PrivilegedAbout,
|
||||||
PrivilegedMozilla,
|
|
||||||
WebLargeAllocation,
|
WebLargeAllocation,
|
||||||
WebCOOPCOEP,
|
|
||||||
// the rest matches GeckoProcessTypes.h
|
// the rest matches GeckoProcessTypes.h
|
||||||
Browser, // Default is named Browser here
|
Browser, // Default is named Browser here
|
||||||
Plugin,
|
Plugin,
|
||||||
|
@ -65,8 +62,6 @@ struct ProcInfo {
|
||||||
dom::ContentParentId childId;
|
dom::ContentParentId childId;
|
||||||
// Process type
|
// Process type
|
||||||
ProcType type;
|
ProcType type;
|
||||||
// Origin, if any
|
|
||||||
nsString origin;
|
|
||||||
// Process filename (without the path name).
|
// Process filename (without the path name).
|
||||||
nsString filename;
|
nsString filename;
|
||||||
// VMS in bytes.
|
// VMS in bytes.
|
||||||
|
@ -91,13 +86,11 @@ typedef MozPromise<ProcInfo, nsresult, true> ProcInfoPromise;
|
||||||
*/
|
*/
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
||||||
const ProcType& processType,
|
const ProcType& type,
|
||||||
const nsAString& origin,
|
|
||||||
mach_port_t aChildTask = MACH_PORT_NULL);
|
mach_port_t aChildTask = MACH_PORT_NULL);
|
||||||
#else
|
#else
|
||||||
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
||||||
const ProcType& processType,
|
const ProcType& type);
|
||||||
const nsAString& origin);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId, const ProcType& type,
|
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId, const ProcType& type,
|
||||||
const nsAString& origin, mach_port_t aChildTask) {
|
mach_port_t aChildTask) {
|
||||||
auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
|
auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
|
||||||
RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
|
RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
|
||||||
|
|
||||||
|
@ -32,15 +32,11 @@ RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId, const
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the string is still alive when `ResolveGetProcInfo` is called.
|
auto ResolveGetProcinfo = [holder = std::move(holder), pid, type, childId, aChildTask]() {
|
||||||
nsString originCopy(origin);
|
|
||||||
auto ResolveGetProcinfo = [holder = std::move(holder), pid, type,
|
|
||||||
originCopy = std::move(originCopy), childId, aChildTask]() {
|
|
||||||
ProcInfo info;
|
ProcInfo info;
|
||||||
info.pid = pid;
|
info.pid = pid;
|
||||||
info.childId = childId;
|
info.childId = childId;
|
||||||
info.type = type;
|
info.type = type;
|
||||||
info.origin = originCopy;
|
|
||||||
struct proc_bsdinfo proc;
|
struct proc_bsdinfo proc;
|
||||||
if ((unsigned long)proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE) <
|
if ((unsigned long)proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE) <
|
||||||
PROC_PIDTBSDINFO_SIZE) {
|
PROC_PIDTBSDINFO_SIZE) {
|
||||||
|
|
|
@ -208,8 +208,7 @@ class ThreadInfoReader final : public StatReader {
|
||||||
};
|
};
|
||||||
|
|
||||||
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
||||||
const ProcType& type,
|
const ProcType& type) {
|
||||||
const nsAString& origin) {
|
|
||||||
auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
|
auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
|
||||||
RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
|
RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
|
@ -221,11 +220,8 @@ RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the string is still alive when the runnable is called.
|
|
||||||
nsString originCopy(origin);
|
|
||||||
RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||||
__func__, [holder = std::move(holder), pid, type,
|
__func__, [holder = std::move(holder), pid, type, childId]() {
|
||||||
originCopy = std::move(originCopy), childId]() {
|
|
||||||
// opening the stat file and reading its content
|
// opening the stat file and reading its content
|
||||||
StatReader reader(pid);
|
StatReader reader(pid);
|
||||||
ProcInfo info;
|
ProcInfo info;
|
||||||
|
@ -237,7 +233,6 @@ RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
||||||
// Extra info
|
// Extra info
|
||||||
info.childId = childId;
|
info.childId = childId;
|
||||||
info.type = type;
|
info.type = type;
|
||||||
info.origin = originCopy;
|
|
||||||
|
|
||||||
// Let's look at the threads
|
// Let's look at the threads
|
||||||
nsCString taskPath;
|
nsCString taskPath;
|
||||||
|
|
|
@ -8,7 +8,6 @@ const { AppConstants } = ChromeUtils.import(
|
||||||
"resource://gre/modules/AppConstants.jsm"
|
"resource://gre/modules/AppConstants.jsm"
|
||||||
);
|
);
|
||||||
const MAC = AppConstants.platform == "macosx";
|
const MAC = AppConstants.platform == "macosx";
|
||||||
const isFissionEnabled = Services.prefs.getBoolPref("fission.autostart");
|
|
||||||
|
|
||||||
add_task(async function test_proc_info() {
|
add_task(async function test_proc_info() {
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
|
@ -43,13 +42,6 @@ add_task(async function test_proc_info() {
|
||||||
"unknown",
|
"unknown",
|
||||||
"Child proc type should be known"
|
"Child proc type should be known"
|
||||||
);
|
);
|
||||||
if (childProc.type == "webIsolated") {
|
|
||||||
Assert.notEqual(
|
|
||||||
childProc.origin || "",
|
|
||||||
"",
|
|
||||||
"Child process should have an origin"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var y = 0; y < childProc.threads.length; y++) {
|
for (var y = 0; y < childProc.threads.length; y++) {
|
||||||
cpuThreads += childProc.threads[y].cpuUser;
|
cpuThreads += childProc.threads[y].cpuUser;
|
||||||
|
|
|
@ -73,7 +73,6 @@ void AppendThreads(ProcInfo* info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
||||||
const nsString& origin,
|
|
||||||
const ProcType& type) {
|
const ProcType& type) {
|
||||||
auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
|
auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
|
||||||
RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
|
RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
|
||||||
|
@ -87,12 +86,8 @@ RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the string is still alive when `ResolveGetProcInfo` is called.
|
|
||||||
nsString originCopy(origin);
|
|
||||||
RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||||
__func__,
|
__func__, [holder = std::move(holder), pid, type, childId]() -> void {
|
||||||
[holder = std::move(holder), originCopy = std::move(originCopy), pid,
|
|
||||||
type, childId]() -> void {
|
|
||||||
nsAutoHandle handle(OpenProcess(
|
nsAutoHandle handle(OpenProcess(
|
||||||
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
|
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
|
||||||
|
|
||||||
|
@ -123,7 +118,6 @@ RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
|
||||||
info.pid = pid;
|
info.pid = pid;
|
||||||
info.childId = childId;
|
info.childId = childId;
|
||||||
info.type = type;
|
info.type = type;
|
||||||
info.origin = originCopy;
|
|
||||||
info.filename.Assign(filename);
|
info.filename.Assign(filename);
|
||||||
info.cpuKernel = ToNanoSeconds(kernelTime);
|
info.cpuKernel = ToNanoSeconds(kernelTime);
|
||||||
info.cpuUser = ToNanoSeconds(userTime);
|
info.cpuUser = ToNanoSeconds(userTime);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче