Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add memory to app.getAppMetrics() #18831

Merged
merged 1 commit into from Jul 23, 2019
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -0,0 +1,9 @@
# MemoryInfo Object

* `workingSetSize` Integer - The amount of memory currently pinned to actual physical RAM.
* `peakWorkingSetSize` Integer - The maximum amount of memory that has ever been pinned
to actual physical RAM.
* `privateBytes` Integer (optional) _Windows_ - The amount of memory not shared by other processes, such as
JS heap or HTML content.

Note that all statistics are reported in Kilobytes.
@@ -7,6 +7,7 @@
The time is represented as number of milliseconds since epoch.
Since the `pid` can be reused after a process dies,
it is useful to use both the `pid` and the `creationTime` to uniquely identify a process.
* `memory` [MemoryInfo](memory-info.md) - Memory information for the process.
* `sandboxed` Boolean (optional) _macOS_ _Windows_ - Whether the process is sandboxed on OS level.
* `integrityLevel` String (optional) _Windows_ - One of the following values:
* `untrusted`
@@ -86,6 +86,7 @@ auto_filenames = {
"docs/api/structures/jump-list-category.md",
"docs/api/structures/jump-list-item.md",
"docs/api/structures/keyboard-event.md",
"docs/api/structures/memory-info.md",
"docs/api/structures/memory-usage-details.md",
"docs/api/structures/mime-typed-buffer.md",
"docs/api/structures/notification-action.md",
@@ -1,3 +1,4 @@
import * as fs from 'fs'
import * as path from 'path'

import { deprecate, Menu } from 'electron'
@@ -66,6 +67,43 @@ if (process.platform === 'darwin') {
app.dock!.getMenu = () => dockMenu
}

if (process.platform === 'linux') {
const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m
const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m

const getStatus = (pid: number) => {
try {
return fs.readFileSync(`/proc/${pid}/status`, 'utf8')
} catch {
return ''
}
}

const getEntry = (file: string, pattern: RegExp) => {
const match = file.match(pattern)
return match ? parseInt(match[1], 10) : 0
}

const getProcessMemoryInfo = (pid: number) => {
const file = getStatus(pid)

return {
workingSetSize: getEntry(file, patternVmRSS),
peakWorkingSetSize: getEntry(file, patternVmHWM)
}
}

const nativeFn = app.getAppMetrics
app.getAppMetrics = () => {
const metrics = nativeFn.call(app)
for (const metric of metrics) {
metric.memory = getProcessMemoryInfo(metric.pid)
}

return metrics
}
}

// Routes the events to webContents.
const events = ['login', 'certificate-error', 'select-client-certificate']
for (const name of events) {
@@ -1218,6 +1218,25 @@ std::vector<mate::Dictionary> App::GetAppMetrics(v8::Isolate* isolate) {
pid_dict.Set("creationTime",
process_metric.second->process.CreationTime().ToJsTime());

#if !defined(OS_LINUX)
auto memory_info = process_metric.second->GetMemoryInfo();

mate::Dictionary memory_dict = mate::Dictionary::CreateEmpty(isolate);
memory_dict.SetHidden("simple", true);
memory_dict.Set("workingSetSize",
static_cast<double>(memory_info.working_set_size >> 10));
memory_dict.Set(
"peakWorkingSetSize",
static_cast<double>(memory_info.peak_working_set_size >> 10));

#if defined(OS_WIN)
memory_dict.Set("privateBytes",
static_cast<double>(memory_info.private_bytes >> 10));
#endif

pid_dict.Set("memory", memory_dict);
#endif

#if defined(OS_MACOSX)
pid_dict.Set("sandboxed", process_metric.second->IsSandboxed());
#elif defined(OS_WIN)
@@ -7,13 +7,46 @@
#include <memory>
#include <utility>

#include "base/optional.h"

#if defined(OS_WIN)
#include <windows.h>

#include <psapi.h>
#include "base/win/win_util.h"
#endif

#if defined(OS_MACOSX)
#include <mach/mach.h>
#include "base/process/port_provider_mac.h"
#include "content/public/browser/browser_child_process_host.h"

extern "C" int sandbox_check(pid_t pid, const char* operation, int type, ...);
#endif

namespace {

mach_port_t TaskForPid(pid_t pid) {
mach_port_t task = MACH_PORT_NULL;
if (auto* port_provider = content::BrowserChildProcessHost::GetPortProvider())
task = port_provider->TaskForPid(pid);
if (task == MACH_PORT_NULL && pid == getpid())
task = mach_task_self();
return task;
}

base::Optional<mach_task_basic_info_data_t> GetTaskInfo(mach_port_t task) {
if (task == MACH_PORT_NULL)
return base::nullopt;
mach_task_basic_info_data_t info = {};
mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
kern_return_t kr = task_info(task, MACH_TASK_BASIC_INFO,
reinterpret_cast<task_info_t>(&info), &count);
return (kr == KERN_SUCCESS) ? base::make_optional(info) : base::nullopt;
}

} // namespace

#endif // defined(OS_MACOSX)

namespace electron {

@@ -37,6 +70,21 @@ ProcessMetric::~ProcessMetric() = default;

#if defined(OS_WIN)

ProcessMemoryInfo ProcessMetric::GetMemoryInfo() const {
ProcessMemoryInfo result;

PROCESS_MEMORY_COUNTERS_EX info = {};
if (::GetProcessMemoryInfo(process.Handle(),
reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&info),
sizeof(info))) {
result.working_set_size = info.WorkingSetSize;
result.peak_working_set_size = info.PeakWorkingSetSize;
result.private_bytes = info.PrivateUsage;
}

return result;
}

ProcessIntegrityLevel ProcessMetric::GetIntegrityLevel() const {
HANDLE token = nullptr;
if (!::OpenProcessToken(process.Handle(), TOKEN_QUERY, &token)) {
@@ -96,6 +144,17 @@ bool ProcessMetric::IsSandboxed(ProcessIntegrityLevel integrity_level) {

#elif defined(OS_MACOSX)

ProcessMemoryInfo ProcessMetric::GetMemoryInfo() const {
ProcessMemoryInfo result;

if (auto info = GetTaskInfo(TaskForPid(process.Pid()))) {
result.working_set_size = info->resident_size;
result.peak_working_set_size = info->resident_size_max;
}

return result;
}

bool ProcessMetric::IsSandboxed() const {
#if defined(MAS_BUILD)
return true;
@@ -13,6 +13,16 @@

namespace electron {

#if !defined(OS_LINUX)
struct ProcessMemoryInfo {
size_t working_set_size = 0;
size_t peak_working_set_size = 0;
#if defined(OS_WIN)
size_t private_bytes = 0;
#endif
};
#endif

#if defined(OS_WIN)
enum class ProcessIntegrityLevel {
Unknown,
@@ -33,6 +43,10 @@ struct ProcessMetric {
std::unique_ptr<base::ProcessMetrics> metrics);
~ProcessMetric();

#if !defined(OS_LINUX)
ProcessMemoryInfo GetMemoryInfo() const;
#endif

#if defined(OS_WIN)
ProcessIntegrityLevel GetIntegrityLevel() const;
static bool IsSandboxed(ProcessIntegrityLevel integrity_level);
@@ -966,6 +966,13 @@ describe('app module', () => {
expect(entry.cpu).to.have.ownProperty('percentCPUUsage').that.is.a('number')
expect(entry.cpu).to.have.ownProperty('idleWakeupsPerSecond').that.is.a('number')

expect(entry.memory).to.have.property('workingSetSize').that.is.greaterThan(0)
expect(entry.memory).to.have.property('peakWorkingSetSize').that.is.greaterThan(0)

if (process.platform === 'win32') {
expect(entry.memory).to.have.property('privateBytes').that.is.greaterThan(0)
}

if (process.platform !== 'linux') {
expect(entry.sandboxed).to.be.a('boolean')
This conversation was marked as resolved by miniak

This comment has been minimized.

Copy link
@codebytere

codebytere Jun 18, 2019

Member

same as above

}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.