зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1734597 - Make about:processes fallback to cycle count to decide if a thread or process is active to workaround low CPU timing precision on Windows, r=dthayer,fluent-reviewers,flod.
Differential Revision: https://phabricator.services.mozilla.com/D127813
This commit is contained in:
Родитель
745866d09d
Коммит
19119e94a7
|
@ -631,6 +631,7 @@ enum WebIDLProcType {
|
|||
dictionary ThreadInfoDictionary {
|
||||
long long tid = 0;
|
||||
DOMString name = "";
|
||||
unsigned long long cpuCycleCount = 0;
|
||||
unsigned long long cpuUser = 0;
|
||||
unsigned long long cpuKernel = 0;
|
||||
};
|
||||
|
@ -684,6 +685,11 @@ dictionary ChildProcInfoDictionary {
|
|||
// Time spent by the process in kernel mode, in ns.
|
||||
unsigned long long cpuKernel = 0;
|
||||
|
||||
// Total CPU cycles used by this process.
|
||||
// On Windows where the resolution of CPU timings is 16ms, this can
|
||||
// be used to determine if a process is idle or slightly active.
|
||||
unsigned long long cpuCycleCount = 0;
|
||||
|
||||
// Thread information for this process.
|
||||
sequence<ThreadInfoDictionary> threads = [];
|
||||
|
||||
|
@ -727,6 +733,11 @@ dictionary ParentProcInfoDictionary {
|
|||
// Time spent by the process in kernel mode, in ns.
|
||||
unsigned long long cpuKernel = 0;
|
||||
|
||||
// Total CPU cycles used by this process.
|
||||
// On Windows where the resolution of CPU timings is 16ms, this can
|
||||
// be used to determine if a process is idle or slightly active.
|
||||
unsigned long long cpuCycleCount = 0;
|
||||
|
||||
// Thread information for this process.
|
||||
sequence<ThreadInfoDictionary> threads = [];
|
||||
|
||||
|
|
|
@ -34,6 +34,9 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
const { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ContextualIdentityService:
|
||||
|
@ -219,15 +222,10 @@ var State = {
|
|||
let result = {
|
||||
tid: cur.tid,
|
||||
name: cur.name || `(${cur.tid})`,
|
||||
// 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,
|
||||
// Total amount of CPU used, in ns (user + kernel).
|
||||
totalCpu: cur.cpuUser + cur.cpuKernel,
|
||||
slopeCpu: null,
|
||||
active: null,
|
||||
};
|
||||
if (!prev) {
|
||||
return result;
|
||||
|
@ -235,9 +233,9 @@ var State = {
|
|||
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;
|
||||
result.slopeCpu = result.slopeCpuKernel + result.slopeCpuUser;
|
||||
result.slopeCpu =
|
||||
(cur.cpuUser + cur.cpuKernel - prev.cpuUser - prev.cpuKernel) / deltaT;
|
||||
result.active = !!result.slopeCpu || cur.cpuCycleCount > prev.cpuCycleCount;
|
||||
return result;
|
||||
},
|
||||
|
||||
|
@ -314,12 +312,9 @@ var State = {
|
|||
filename: cur.filename,
|
||||
totalRamSize: cur.memory,
|
||||
deltaRamSize: null,
|
||||
totalCpuUser: cur.cpuUser,
|
||||
slopeCpuUser: null,
|
||||
totalCpuKernel: cur.cpuKernel,
|
||||
slopeCpuKernel: null,
|
||||
totalCpu: cur.cpuUser + cur.cpuKernel,
|
||||
slopeCpu: null,
|
||||
active: null,
|
||||
type: cur.type,
|
||||
origin: cur.origin || "",
|
||||
threads: null,
|
||||
|
@ -366,9 +361,9 @@ var State = {
|
|||
});
|
||||
}
|
||||
result.deltaRamSize = cur.memory - prev.memory;
|
||||
result.slopeCpuUser = (cur.cpuUser - prev.cpuUser) / deltaT;
|
||||
result.slopeCpuKernel = (cur.cpuKernel - prev.cpuKernel) / deltaT;
|
||||
result.slopeCpu = result.slopeCpuUser + result.slopeCpuKernel;
|
||||
result.slopeCpu =
|
||||
(cur.cpuUser + cur.cpuKernel - prev.cpuUser - prev.cpuKernel) / deltaT;
|
||||
result.active = !!result.slopeCpu || cur.cpuCycleCount > prev.cpuCycleCount;
|
||||
result.threads = threads;
|
||||
return result;
|
||||
},
|
||||
|
@ -471,6 +466,46 @@ var View = {
|
|||
return row;
|
||||
},
|
||||
|
||||
displayCpu(data, cpuCell) {
|
||||
if (data.slopeCpu == null) {
|
||||
this._fillCell(cpuCell, {
|
||||
fluentName: "about-processes-cpu-user-and-kernel-not-ready",
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else {
|
||||
let { duration, unit } = this._getDuration(data.totalCpu);
|
||||
if (data.totalCpu == 0 && AppConstants.platform == "win") {
|
||||
// The minimum non zero CPU time we can get on Windows is 16ms
|
||||
// so avoid displaying '0ns'.
|
||||
unit = "ms";
|
||||
}
|
||||
let localizedUnit = gLocalizedUnits.duration[unit];
|
||||
if (data.slopeCpu == 0) {
|
||||
let fluentName = data.active
|
||||
? "about-processes-cpu-almost-idle"
|
||||
: "about-processes-cpu-fully-idle";
|
||||
this._fillCell(cpuCell, {
|
||||
fluentName,
|
||||
fluentArgs: {
|
||||
total: duration,
|
||||
unit: localizedUnit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else {
|
||||
this._fillCell(cpuCell, {
|
||||
fluentName: "about-processes-cpu",
|
||||
fluentArgs: {
|
||||
percent: data.slopeCpu,
|
||||
total: duration,
|
||||
unit: localizedUnit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Display a row showing a single process (without its threads).
|
||||
*
|
||||
|
@ -682,37 +717,9 @@ var View = {
|
|||
}
|
||||
}
|
||||
|
||||
// Column: CPU: User and Kernel
|
||||
// Column: CPU
|
||||
let cpuCell = memoryCell.nextSibling;
|
||||
if (data.slopeCpu == null) {
|
||||
this._fillCell(cpuCell, {
|
||||
fluentName: "about-processes-cpu-user-and-kernel-not-ready",
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else {
|
||||
let { duration, unit } = this._getDuration(data.totalCpu);
|
||||
let localizedUnit = gLocalizedUnits.duration[unit];
|
||||
if (data.slopeCpu == 0) {
|
||||
this._fillCell(cpuCell, {
|
||||
fluentName: "about-processes-cpu-idle",
|
||||
fluentArgs: {
|
||||
total: duration,
|
||||
unit: localizedUnit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else {
|
||||
this._fillCell(cpuCell, {
|
||||
fluentName: "about-processes-cpu",
|
||||
fluentArgs: {
|
||||
percent: data.slopeCpu,
|
||||
total: duration,
|
||||
unit: localizedUnit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
}
|
||||
}
|
||||
this.displayCpu(data, cpuCell);
|
||||
|
||||
// Column: Kill button – but not for all processes.
|
||||
let killButton = cpuCell.nextSibling;
|
||||
|
@ -764,7 +771,7 @@ var View = {
|
|||
let activeThreads = new Map();
|
||||
let activeThreadCount = 0;
|
||||
for (let t of data.threads) {
|
||||
if (!t.slopeCpu) {
|
||||
if (!t.active) {
|
||||
continue;
|
||||
}
|
||||
++activeThreadCount;
|
||||
|
@ -928,37 +935,8 @@ var View = {
|
|||
classes: ["name", "double_indent"],
|
||||
});
|
||||
|
||||
// Column: CPU: User and Kernel
|
||||
let cpuCell = nameCell.nextSibling;
|
||||
if (data.slopeCpu == null) {
|
||||
this._fillCell(cpuCell, {
|
||||
fluentName: "about-processes-cpu-user-and-kernel-not-ready",
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else {
|
||||
let { duration, unit } = this._getDuration(data.totalCpu);
|
||||
let localizedUnit = gLocalizedUnits.duration[unit];
|
||||
if (data.slopeCpu == 0) {
|
||||
this._fillCell(cpuCell, {
|
||||
fluentName: "about-processes-cpu-idle",
|
||||
fluentArgs: {
|
||||
total: duration,
|
||||
unit: localizedUnit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else {
|
||||
this._fillCell(cpuCell, {
|
||||
fluentName: "about-processes-cpu",
|
||||
fluentArgs: {
|
||||
percent: data.slopeCpu,
|
||||
total: duration,
|
||||
unit: localizedUnit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
}
|
||||
}
|
||||
// Column: CPU
|
||||
this.displayCpu(data, nameCell.nextSibling);
|
||||
|
||||
// Third column (Buttons) is empty, nothing to do.
|
||||
},
|
||||
|
@ -1338,6 +1316,11 @@ var Control = {
|
|||
// Used by tests to differentiate full updates from l10n updates.
|
||||
document.dispatchEvent(new CustomEvent("AboutProcessesUpdated"));
|
||||
},
|
||||
_compareCpu(a, b) {
|
||||
return (
|
||||
b.slopeCpu - a.slopeCpu || b.active - a.active || b.totalCpu - a.totalCpu
|
||||
);
|
||||
},
|
||||
_showThreads(row) {
|
||||
let process = row.process;
|
||||
this._sortThreads(process.threads);
|
||||
|
@ -1353,7 +1336,7 @@ var Control = {
|
|||
order = a.name.localeCompare(b.name) || a.tid - b.tid;
|
||||
break;
|
||||
case "column-cpu-total":
|
||||
order = b.slopeCpu - a.slopeCpu;
|
||||
order = this._compareCpu(a, b);
|
||||
break;
|
||||
case "column-memory-resident":
|
||||
case null:
|
||||
|
@ -1379,7 +1362,7 @@ var Control = {
|
|||
a.pid - b.pid;
|
||||
break;
|
||||
case "column-cpu-total":
|
||||
order = b.slopeCpu - a.slopeCpu;
|
||||
order = this._compareCpu(a, b);
|
||||
break;
|
||||
case "column-memory-resident":
|
||||
order = b.totalRamSize - a.totalRamSize;
|
||||
|
|
|
@ -634,7 +634,7 @@ async function testAboutProcessesWithConfig({ showAllFrames, showThreads }) {
|
|||
number,
|
||||
"The number of active threads should not exceed the total number of threads"
|
||||
);
|
||||
let activeThreads = row.process.threads.filter(t => t.slopeCpu);
|
||||
let activeThreads = row.process.threads.filter(t => t.active);
|
||||
Assert.equal(
|
||||
active,
|
||||
activeThreads.length,
|
||||
|
|
|
@ -60,6 +60,7 @@ struct ThreadInfo {
|
|||
uint64_t cpuUser = 0;
|
||||
// System time in ns.
|
||||
uint64_t cpuKernel = 0;
|
||||
uint64_t cpuCycleCount = 0;
|
||||
};
|
||||
|
||||
// Info on a DOM window.
|
||||
|
@ -111,6 +112,7 @@ struct ProcInfo {
|
|||
uint64_t cpuUser = 0;
|
||||
// System time in ns.
|
||||
uint64_t cpuKernel = 0;
|
||||
uint64_t cpuCycleCount = 0;
|
||||
// Threads owned by this process.
|
||||
CopyableTArray<ThreadInfo> threads;
|
||||
// DOM windows represented by this process.
|
||||
|
@ -213,6 +215,7 @@ nsresult CopySysProcInfoToDOM(const ProcInfo& source, T* dest) {
|
|||
dest->mMemory = source.memory;
|
||||
dest->mCpuUser = source.cpuUser;
|
||||
dest->mCpuKernel = source.cpuKernel;
|
||||
dest->mCpuCycleCount = source.cpuCycleCount;
|
||||
|
||||
// Copy thread info.
|
||||
mozilla::dom::Sequence<mozilla::dom::ThreadInfoDictionary> threads;
|
||||
|
@ -224,6 +227,7 @@ nsresult CopySysProcInfoToDOM(const ProcInfo& source, T* dest) {
|
|||
}
|
||||
thread->mCpuUser = entry.cpuUser;
|
||||
thread->mCpuKernel = entry.cpuKernel;
|
||||
thread->mCpuCycleCount = entry.cpuCycleCount;
|
||||
thread->mTid = entry.tid;
|
||||
thread->mName.Assign(entry.name);
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@ RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests) {
|
|||
info.filename.Assign(filename);
|
||||
info.cpuKernel = ToNanoSeconds(kernelTime);
|
||||
info.cpuUser = ToNanoSeconds(userTime);
|
||||
QueryProcessCycleTime(handle.get(), &info.cpuCycleCount);
|
||||
|
||||
info.memory = memoryCounters.PrivateUsage;
|
||||
|
||||
if (!gathered.put(request.pid, std::move(info))) {
|
||||
|
@ -157,6 +159,8 @@ RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests) {
|
|||
threadInfo->cpuUser = ToNanoSeconds(userTime);
|
||||
}
|
||||
|
||||
QueryThreadCycleTime(hThread.get(), &threadInfo->cpuCycleCount);
|
||||
|
||||
// Attempt to get thread name.
|
||||
// If we fail, continue without this piece of information.
|
||||
if (getThreadDescription) {
|
||||
|
|
|
@ -138,9 +138,14 @@ about-processes-cpu = { NUMBER($percent, maximumSignificantDigits: 2, style: "pe
|
|||
# Special case: data is not available yet.
|
||||
about-processes-cpu-user-and-kernel-not-ready = (measuring)
|
||||
|
||||
# Special case: process or thread is almost idle (using less than 0.1% of a CPU core).
|
||||
# This case only occurs on Windows where the precision of the CPU times is low.
|
||||
about-processes-cpu-almost-idle = < 0.1%
|
||||
.title = Total CPU time: { NUMBER($total, maximumFractionDigits: 0) }{ $unit }
|
||||
|
||||
# Special case: process or thread is currently idle.
|
||||
about-processes-cpu-idle = idle
|
||||
.title = Total CPU time: { NUMBER($total, maximumFractionDigits: 2) }{ $unit }
|
||||
about-processes-cpu-fully-idle = idle
|
||||
.title = Total CPU time: { NUMBER($total, maximumFractionDigits: 0) }{ $unit }
|
||||
|
||||
## Displaying Memory (total and delta)
|
||||
## Variables:
|
||||
|
|
Загрузка…
Ссылка в новой задаче