feat: add creationTime / sandboxed / integrityLevel to app.getAppMetrics() (#18718)
This is useful for checking which processes are sandboxed on OS level. Regarding creationTime, 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.
This commit is contained in:
parent
0bdc05bf24
commit
d9215dd4ce
7 changed files with 222 additions and 49 deletions
|
@ -69,6 +69,25 @@ using atom::Browser;
|
|||
namespace mate {
|
||||
|
||||
#if defined(OS_WIN)
|
||||
template <>
|
||||
struct Converter<atom::ProcessIntegrityLevel> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
atom::ProcessIntegrityLevel value) {
|
||||
switch (value) {
|
||||
case atom::ProcessIntegrityLevel::Untrusted:
|
||||
return mate::StringToV8(isolate, "untrusted");
|
||||
case atom::ProcessIntegrityLevel::Low:
|
||||
return mate::StringToV8(isolate, "low");
|
||||
case atom::ProcessIntegrityLevel::Medium:
|
||||
return mate::StringToV8(isolate, "medium");
|
||||
case atom::ProcessIntegrityLevel::High:
|
||||
return mate::StringToV8(isolate, "high");
|
||||
default:
|
||||
return mate::StringToV8(isolate, "unknown");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<Browser::UserTask> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
|
@ -357,31 +376,10 @@ struct Converter<content::CertificateRequestResultType> {
|
|||
|
||||
namespace atom {
|
||||
|
||||
ProcessMetric::ProcessMetric(int type,
|
||||
base::ProcessId pid,
|
||||
std::unique_ptr<base::ProcessMetrics> metrics) {
|
||||
this->type = type;
|
||||
this->pid = pid;
|
||||
this->metrics = std::move(metrics);
|
||||
}
|
||||
|
||||
ProcessMetric::~ProcessMetric() = default;
|
||||
|
||||
namespace api {
|
||||
|
||||
namespace {
|
||||
|
||||
class AppIdProcessIterator : public base::ProcessIterator {
|
||||
public:
|
||||
AppIdProcessIterator() : base::ProcessIterator(nullptr) {}
|
||||
|
||||
protected:
|
||||
bool IncludeEntry() override {
|
||||
return (entry().parent_pid() == base::GetCurrentProcId() ||
|
||||
entry().pid() == base::GetCurrentProcId());
|
||||
}
|
||||
};
|
||||
|
||||
IconLoader::IconSize GetIconSizeByString(const std::string& size) {
|
||||
if (size == "small") {
|
||||
return IconLoader::IconSize::SMALL;
|
||||
|
@ -550,7 +548,7 @@ App::App(v8::Isolate* isolate) {
|
|||
|
||||
base::ProcessId pid = base::GetCurrentProcId();
|
||||
auto process_metric = std::make_unique<atom::ProcessMetric>(
|
||||
content::PROCESS_TYPE_BROWSER, pid,
|
||||
content::PROCESS_TYPE_BROWSER, base::GetCurrentProcessHandle(),
|
||||
base::ProcessMetrics::CreateCurrentProcessMetrics());
|
||||
app_metrics_[pid] = std::move(process_metric);
|
||||
Init(isolate);
|
||||
|
@ -825,15 +823,13 @@ void App::ChildProcessLaunched(int process_type, base::ProcessHandle handle) {
|
|||
auto pid = base::GetProcId(handle);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
std::unique_ptr<base::ProcessMetrics> metrics(
|
||||
base::ProcessMetrics::CreateProcessMetrics(
|
||||
handle, content::BrowserChildProcessHost::GetPortProvider()));
|
||||
auto metrics = base::ProcessMetrics::CreateProcessMetrics(
|
||||
handle, content::BrowserChildProcessHost::GetPortProvider());
|
||||
#else
|
||||
std::unique_ptr<base::ProcessMetrics> metrics(
|
||||
base::ProcessMetrics::CreateProcessMetrics(handle));
|
||||
auto metrics = base::ProcessMetrics::CreateProcessMetrics(handle);
|
||||
#endif
|
||||
app_metrics_[pid] = std::make_unique<atom::ProcessMetric>(process_type, pid,
|
||||
std::move(metrics));
|
||||
app_metrics_[pid] = std::make_unique<atom::ProcessMetric>(
|
||||
process_type, handle, std::move(metrics));
|
||||
}
|
||||
|
||||
void App::ChildProcessDisconnected(base::ProcessId pid) {
|
||||
|
@ -1215,9 +1211,21 @@ std::vector<mate::Dictionary> App::GetAppMetrics(v8::Isolate* isolate) {
|
|||
#endif
|
||||
|
||||
pid_dict.Set("cpu", cpu_dict);
|
||||
pid_dict.Set("pid", process_metric.second->pid);
|
||||
pid_dict.Set("pid", process_metric.second->process.Pid());
|
||||
pid_dict.Set("type", content::GetProcessTypeNameInEnglish(
|
||||
process_metric.second->type));
|
||||
pid_dict.Set("creationTime",
|
||||
process_metric.second->process.CreationTime().ToJsTime());
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
pid_dict.Set("sandboxed", process_metric.second->IsSandboxed());
|
||||
#elif defined(OS_WIN)
|
||||
auto integrity_level = process_metric.second->GetIntegrityLevel();
|
||||
auto sandboxed = ProcessMetric::IsSandboxed(integrity_level);
|
||||
pid_dict.Set("integrityLevel", integrity_level);
|
||||
pid_dict.Set("sandboxed", sandboxed);
|
||||
#endif
|
||||
|
||||
result.push_back(pid_dict);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,13 +12,12 @@
|
|||
#include <vector>
|
||||
|
||||
#include "atom/browser/api/event_emitter.h"
|
||||
#include "atom/browser/api/process_metric.h"
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/browser_observer.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "atom/common/promise_util.h"
|
||||
#include "base/process/process_iterator.h"
|
||||
#include "base/process/process_metrics.h"
|
||||
#include "base/task/cancelable_task_tracker.h"
|
||||
#include "chrome/browser/icon_manager.h"
|
||||
#include "chrome/browser/process_singleton.h"
|
||||
|
@ -49,17 +48,6 @@ namespace atom {
|
|||
enum class JumpListResult : int;
|
||||
#endif
|
||||
|
||||
struct ProcessMetric {
|
||||
int type;
|
||||
base::ProcessId pid;
|
||||
std::unique_ptr<base::ProcessMetrics> metrics;
|
||||
|
||||
ProcessMetric(int type,
|
||||
base::ProcessId pid,
|
||||
std::unique_ptr<base::ProcessMetrics> metrics);
|
||||
~ProcessMetric();
|
||||
};
|
||||
|
||||
namespace api {
|
||||
|
||||
class App : public AtomBrowserClient::Delegate,
|
||||
|
|
109
atom/browser/api/process_metric.cc
Normal file
109
atom/browser/api/process_metric.cc
Normal file
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/api/process_metric.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
extern "C" int sandbox_check(pid_t pid, const char* operation, int type, ...);
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
|
||||
ProcessMetric::ProcessMetric(int type,
|
||||
base::ProcessHandle handle,
|
||||
std::unique_ptr<base::ProcessMetrics> metrics) {
|
||||
this->type = type;
|
||||
this->metrics = std::move(metrics);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
HANDLE duplicate_handle = INVALID_HANDLE_VALUE;
|
||||
::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(),
|
||||
&duplicate_handle, 0, false, DUPLICATE_SAME_ACCESS);
|
||||
this->process = base::Process(duplicate_handle);
|
||||
#else
|
||||
this->process = base::Process(handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
ProcessMetric::~ProcessMetric() = default;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
ProcessIntegrityLevel ProcessMetric::GetIntegrityLevel() const {
|
||||
HANDLE token = nullptr;
|
||||
if (!::OpenProcessToken(process.Handle(), TOKEN_QUERY, &token)) {
|
||||
return ProcessIntegrityLevel::Unknown;
|
||||
}
|
||||
|
||||
base::win::ScopedHandle token_scoped(token);
|
||||
|
||||
DWORD token_info_length = 0;
|
||||
if (::GetTokenInformation(token, TokenIntegrityLevel, nullptr, 0,
|
||||
&token_info_length) ||
|
||||
::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
return ProcessIntegrityLevel::Unknown;
|
||||
}
|
||||
|
||||
auto token_label_bytes = std::make_unique<char[]>(token_info_length);
|
||||
TOKEN_MANDATORY_LABEL* token_label =
|
||||
reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get());
|
||||
if (!::GetTokenInformation(token, TokenIntegrityLevel, token_label,
|
||||
token_info_length, &token_info_length)) {
|
||||
return ProcessIntegrityLevel::Unknown;
|
||||
}
|
||||
|
||||
DWORD integrity_level = *::GetSidSubAuthority(
|
||||
token_label->Label.Sid,
|
||||
static_cast<DWORD>(*::GetSidSubAuthorityCount(token_label->Label.Sid) -
|
||||
1));
|
||||
|
||||
if (integrity_level >= SECURITY_MANDATORY_UNTRUSTED_RID &&
|
||||
integrity_level < SECURITY_MANDATORY_LOW_RID) {
|
||||
return ProcessIntegrityLevel::Untrusted;
|
||||
}
|
||||
|
||||
if (integrity_level >= SECURITY_MANDATORY_LOW_RID &&
|
||||
integrity_level < SECURITY_MANDATORY_MEDIUM_RID) {
|
||||
return ProcessIntegrityLevel::Low;
|
||||
}
|
||||
|
||||
if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID &&
|
||||
integrity_level < SECURITY_MANDATORY_HIGH_RID) {
|
||||
return ProcessIntegrityLevel::Medium;
|
||||
}
|
||||
|
||||
if (integrity_level >= SECURITY_MANDATORY_HIGH_RID &&
|
||||
integrity_level < SECURITY_MANDATORY_SYSTEM_RID) {
|
||||
return ProcessIntegrityLevel::High;
|
||||
}
|
||||
|
||||
return ProcessIntegrityLevel::Unknown;
|
||||
}
|
||||
|
||||
// static
|
||||
bool ProcessMetric::IsSandboxed(ProcessIntegrityLevel integrity_level) {
|
||||
return integrity_level > ProcessIntegrityLevel::Unknown &&
|
||||
integrity_level < ProcessIntegrityLevel::Medium;
|
||||
}
|
||||
|
||||
#elif defined(OS_MACOSX)
|
||||
|
||||
bool ProcessMetric::IsSandboxed() const {
|
||||
#if defined(MAS_BUILD)
|
||||
return true;
|
||||
#else
|
||||
return sandbox_check(process.Pid(), nullptr, 0) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
} // namespace atom
|
46
atom/browser/api/process_metric.h
Normal file
46
atom/browser/api/process_metric.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_API_PROCESS_METRIC_H_
|
||||
#define ATOM_BROWSER_API_PROCESS_METRIC_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/process/process.h"
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/process/process_metrics.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
#if defined(OS_WIN)
|
||||
enum class ProcessIntegrityLevel {
|
||||
Unknown,
|
||||
Untrusted,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ProcessMetric {
|
||||
int type;
|
||||
base::Process process;
|
||||
std::unique_ptr<base::ProcessMetrics> metrics;
|
||||
|
||||
ProcessMetric(int type,
|
||||
base::ProcessHandle handle,
|
||||
std::unique_ptr<base::ProcessMetrics> metrics);
|
||||
~ProcessMetric();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
ProcessIntegrityLevel GetIntegrityLevel() const;
|
||||
static bool IsSandboxed(ProcessIntegrityLevel integrity_level);
|
||||
#elif defined(OS_MACOSX)
|
||||
bool IsSandboxed() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_PROCESS_METRIC_H_
|
|
@ -3,3 +3,14 @@
|
|||
* `pid` Integer - Process id of the process.
|
||||
* `type` String - Process type (Browser or Tab or GPU etc).
|
||||
* `cpu` [CPUUsage](cpu-usage.md) - CPU usage of the process.
|
||||
* `creationTime` Number - Creation time for this process.
|
||||
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.
|
||||
* `sandboxed` Boolean (optional) _macOS_ _Windows_ - Whether the process is sandboxed on OS level.
|
||||
* `integrityLevel` String (optional) _Windows_ - One of the following values:
|
||||
* `untrusted`
|
||||
* `low`
|
||||
* `medium`
|
||||
* `high`
|
||||
* `unknown`
|
||||
|
|
|
@ -122,6 +122,8 @@ filenames = {
|
|||
"atom/browser/api/gpu_info_enumerator.h",
|
||||
"atom/browser/api/gpuinfo_manager.cc",
|
||||
"atom/browser/api/gpuinfo_manager.h",
|
||||
"atom/browser/api/process_metric.cc",
|
||||
"atom/browser/api/process_metric.h",
|
||||
"atom/browser/api/save_page_handler.cc",
|
||||
"atom/browser/api/save_page_handler.h",
|
||||
"atom/browser/auto_updater.cc",
|
||||
|
|
|
@ -931,13 +931,22 @@ describe('app module', () => {
|
|||
expect(appMetrics).to.be.an('array').and.have.lengthOf.at.least(1, 'App memory info object is not > 0')
|
||||
|
||||
const types = []
|
||||
for (const { pid, type, cpu } of appMetrics) {
|
||||
expect(pid).to.be.above(0, 'pid is not > 0')
|
||||
expect(type).to.be.a('string').that.does.not.equal('')
|
||||
for (const entry of appMetrics) {
|
||||
expect(entry.pid).to.be.above(0, 'pid is not > 0')
|
||||
expect(entry.type).to.be.a('string').that.does.not.equal('')
|
||||
expect(entry.creationTime).to.be.a('number').that.is.greaterThan(0)
|
||||
|
||||
types.push(type)
|
||||
expect(cpu).to.have.ownProperty('percentCPUUsage').that.is.a('number')
|
||||
expect(cpu).to.have.ownProperty('idleWakeupsPerSecond').that.is.a('number')
|
||||
types.push(entry.type)
|
||||
expect(entry.cpu).to.have.ownProperty('percentCPUUsage').that.is.a('number')
|
||||
expect(entry.cpu).to.have.ownProperty('idleWakeupsPerSecond').that.is.a('number')
|
||||
|
||||
if (process.platform !== 'linux') {
|
||||
expect(entry.sandboxed).to.be.a('boolean')
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
expect(entry.integrityLevel).to.be.a('string')
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
|
|
Loading…
Reference in a new issue