From 939747945e114688355b9fa4bca410076c6628c6 Mon Sep 17 00:00:00 2001 From: Hari Juturu Date: Mon, 15 May 2017 17:41:45 -0700 Subject: [PATCH] Adding CPU & Memory metrics for App --- atom/browser/api/atom_api_app.cc | 112 +++++++++++++++++---- atom/browser/api/atom_api_app.h | 40 +++++++- atom/browser/atom_browser_client.cc | 10 ++ atom/browser/atom_browser_client.h | 1 + docs/api/app.md | 8 +- docs/api/structures/process-memory-info.md | 4 - docs/api/structures/process-metric.md | 6 ++ spec/api-app-spec.js | 11 +- vendor/brightray | 1 + 9 files changed, 159 insertions(+), 34 deletions(-) delete mode 100644 docs/api/structures/process-memory-info.md create mode 100644 docs/api/structures/process-metric.md create mode 160000 vendor/brightray diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index cf5cb4386cb5..30fd54f1ecf1 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -29,12 +29,14 @@ #include "base/files/file_util.h" #include "base/path_service.h" #include "base/strings/string_util.h" +#include "base/sys_info.h" #include "brightray/browser/brightray_paths.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/icon_manager.h" #include "chrome/common/chrome_paths.h" #include "content/public/browser/browser_accessibility_state.h" #include "content/public/browser/browser_child_process_host.h" +#include "content/public/browser/child_process_data.h" #include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/gpu_data_manager.h" #include "content/public/browser/render_frame_host.h" @@ -505,6 +507,19 @@ App::App(v8::Isolate* isolate) { static_cast(AtomBrowserClient::Get())->set_delegate(this); Browser::Get()->AddObserver(this); content::GpuDataManager::GetInstance()->AddObserver(this); + content::BrowserChildProcessObserver::Add(this); + int pid = 0; + #if defined(OS_WIN) + pid = GetCurrentProcessId(); + #elif defined(OS_POSIX) + pid = getpid(); + #endif + std::unique_ptr process_metric( + new atom::ProcessMetric( + "Browser", + pid, + base::ProcessMetrics::CreateCurrentProcessMetrics())); + app_metrics_[pid] = std::move(process_metric); Init(isolate); } @@ -513,6 +528,7 @@ App::~App() { nullptr); Browser::Get()->RemoveObserver(this); content::GpuDataManager::GetInstance()->RemoveObserver(this); + content::BrowserChildProcessObserver::Remove(this); } void App::OnBeforeQuit(bool* prevent_default) { @@ -666,6 +682,58 @@ void App::OnGpuProcessCrashed(base::TerminationStatus status) { status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED); } +void App::BrowserChildProcessLaunchedAndConnected( + const content::ChildProcessData& data) { + this->ChildProcessLaunched( + data.process_type, + base::GetProcId(data.handle)); +} + +void App::BrowserChildProcessHostDisconnected( + const content::ChildProcessData& data) { + this->ChildProcessDisconnected( + base::GetProcId(data.handle)); +} + +void App::RenderProcessReady( + content::RenderProcessHost* host) { + this->ChildProcessLaunched( + content::PROCESS_TYPE_RENDERER, + base::GetProcId(host->GetHandle())); +} + +void App::RenderProcessDisconnected( + content::RenderProcessHost* host) { + this->ChildProcessDisconnected( + base::GetProcId(host->GetHandle())); +} + +void App::ChildProcessLaunched( + int process_type, + base::ProcessId pid) { + auto process = base::Process::OpenWithExtraPrivileges(pid); + +#if defined(OS_MACOSX) + std::unique_ptr metrics( + base::ProcessMetrics::CreateProcessMetrics( + process.Handle(), content::BrowserChildProcessHost::GetPortProvider())); +#else + std::unique_ptr metrics( + base::ProcessMetrics::CreateProcessMetrics(process.Handle())); +#endif + std::unique_ptr process_metric( + new atom::ProcessMetric( + content::GetProcessTypeNameInEnglish(process_type), + pid, + std::move(metrics))); + app_metrics_[pid] = std::move(process_metric); +} + +void App::ChildProcessDisconnected( + base::ProcessId pid) { + app_metrics_.erase(pid); +} + base::FilePath App::GetAppPath() const { return app_path_; } @@ -923,42 +991,40 @@ void App::GetFileIcon(const base::FilePath& path, } } -std::vector App::GetAppMemoryInfo(v8::Isolate* isolate) { - AppIdProcessIterator process_iterator; - auto process_entry = process_iterator.NextProcessEntry(); +std::vector App::GetAppMetrics(v8::Isolate* isolate) { std::vector result; + int processor_count = base::SysInfo::NumberOfProcessors(); - while (process_entry != nullptr) { - int64_t pid = process_entry->pid(); - auto process = base::Process::OpenWithExtraPrivileges(pid); - -#if defined(OS_MACOSX) - std::unique_ptr metrics( - base::ProcessMetrics::CreateProcessMetrics( - process.Handle(), content::BrowserChildProcessHost::GetPortProvider())); -#else - std::unique_ptr metrics( - base::ProcessMetrics::CreateProcessMetrics(process.Handle())); -#endif - + for (const auto& process_metric : app_metrics_) { mate::Dictionary pid_dict = mate::Dictionary::CreateEmpty(isolate); mate::Dictionary memory_dict = mate::Dictionary::CreateEmpty(isolate); + mate::Dictionary cpu_dict = mate::Dictionary::CreateEmpty(isolate); memory_dict.Set("workingSetSize", - static_cast(metrics->GetWorkingSetSize() >> 10)); + static_cast( + process_metric.second->metrics->GetWorkingSetSize() >> 10)); memory_dict.Set("peakWorkingSetSize", - static_cast(metrics->GetPeakWorkingSetSize() >> 10)); + static_cast( + process_metric.second->metrics->GetPeakWorkingSetSize() >> 10)); size_t private_bytes, shared_bytes; - if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes)) { + if (process_metric.second->metrics->GetMemoryBytes( + &private_bytes, + &shared_bytes)) { memory_dict.Set("privateBytes", static_cast(private_bytes >> 10)); memory_dict.Set("sharedBytes", static_cast(shared_bytes >> 10)); } pid_dict.Set("memory", memory_dict); - pid_dict.Set("pid", pid); + cpu_dict.Set("percentCPUUsage", + process_metric.second->metrics->GetPlatformIndependentCPUUsage() + / processor_count); + cpu_dict.Set("idleWakeupsPerSecond", + process_metric.second->metrics->GetIdleWakeupsPerSecond()); + pid_dict.Set("cpu", cpu_dict); + pid_dict.Set("pid", process_metric.second->pid); + pid_dict.Set("type", process_metric.second->type); result.push_back(pid_dict); - process_entry = process_iterator.NextProcessEntry(); } return result; @@ -1036,7 +1102,9 @@ void App::BuildPrototype( .SetMethod("disableHardwareAcceleration", &App::DisableHardwareAcceleration) .SetMethod("getFileIcon", &App::GetFileIcon) - .SetMethod("getAppMemoryInfo", &App::GetAppMemoryInfo); + // TODO(juturu): Deprecate getAppMemoryInfo. + .SetMethod("getAppMemoryInfo", &App::GetAppMetrics) + .SetMethod("getAppMetrics", &App::GetAppMetrics); } } // namespace api diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index 9543e4b74b22..30f7edcbc9b8 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -17,7 +17,9 @@ #include "base/task/cancelable_task_tracker.h" #include "chrome/browser/icon_manager.h" #include "chrome/browser/process_singleton.h" +#include "content/public/browser/browser_child_process_observer.h" #include "content/public/browser/gpu_data_manager_observer.h" +#include "content/public/browser/render_process_host.h" #include "native_mate/dictionary.h" #include "native_mate/handle.h" #include "net/base/completion_callback.h" @@ -40,12 +42,26 @@ namespace atom { enum class JumpListResult : int; #endif +struct ProcessMetric { + std::string type; + base::ProcessId pid; + std::unique_ptr metrics; + ProcessMetric(std::string type, + base::ProcessId pid, + std::unique_ptr metrics) { + this->type = type; + this->pid = pid; + this->metrics = std::move(metrics); + } +}; + namespace api { class App : public AtomBrowserClient::Delegate, public mate::EventEmitter, public BrowserObserver, - public content::GpuDataManagerObserver { + public content::GpuDataManagerObserver, + public content::BrowserChildProcessObserver { public: using FileIconCallback = base::Callback, const gfx::Image&)>; @@ -73,6 +89,10 @@ class App : public AtomBrowserClient::Delegate, #endif base::FilePath GetAppPath() const; + void RenderProcessReady( + content::RenderProcessHost* host); + void RenderProcessDisconnected( + content::RenderProcessHost* host); protected: explicit App(v8::Isolate* isolate); @@ -118,8 +138,19 @@ class App : public AtomBrowserClient::Delegate, // content::GpuDataManagerObserver: void OnGpuProcessCrashed(base::TerminationStatus status) override; + // content::BrowserChildProcessObserver: + void BrowserChildProcessLaunchedAndConnected( + const content::ChildProcessData& data) override; + void BrowserChildProcessHostDisconnected( + const content::ChildProcessData& data) override; + private: void SetAppPath(const base::FilePath& app_path); + void ChildProcessLaunched( + int process_type, + base::ProcessId id); + void ChildProcessDisconnected( + base::ProcessId pid); // Get/Set the pre-defined path in PathService. base::FilePath GetPath(mate::Arguments* args, const std::string& name); @@ -143,7 +174,7 @@ class App : public AtomBrowserClient::Delegate, void GetFileIcon(const base::FilePath& path, mate::Arguments* args); - std::vector GetAppMemoryInfo(v8::Isolate* isolate); + std::vector GetAppMetrics(v8::Isolate* isolate); #if defined(OS_WIN) // Get the current Jump List settings. @@ -164,6 +195,11 @@ class App : public AtomBrowserClient::Delegate, base::FilePath app_path_; + using ProcessMetricMap = + std::unordered_map>; + ProcessMetricMap app_metrics_; + DISALLOW_COPY_AND_ASSIGN(App); }; diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 6cede9c0112c..ad77ba814c61 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -408,6 +408,16 @@ void AtomBrowserClient::RenderProcessHostDestroyed( } } RemoveProcessPreferences(process_id); + if (delegate_) { + static_cast(delegate_)->RenderProcessDisconnected(host); + } +} + +void AtomBrowserClient::RenderProcessReady( + content::RenderProcessHost* host) { + if (delegate_) { + static_cast(delegate_)->RenderProcessReady(host); + } } } // namespace atom diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index c9a58981a2ce..ad18885d29a1 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -109,6 +109,7 @@ class AtomBrowserClient : public brightray::BrowserClient, // content::RenderProcessHostObserver: void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; + void RenderProcessReady(content::RenderProcessHost* host) override; private: bool ShouldCreateNewSiteInstance(content::RenderFrameHost* render_frame_host, diff --git a/docs/api/app.md b/docs/api/app.md index 614989dc99c6..f447d25491dc 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -762,9 +762,13 @@ Disables hardware acceleration for current app. This method can only be called before app is ready. -### `app.getAppMemoryInfo()` +### `app.getAppMemoryInfo()` _Deprecate_ -Returns [ProcessMemoryInfo[]](structures/process-memory-info.md): Array of `ProcessMemoryInfo` objects that correspond to memory usage statistics of all the processes associated with the app. +Returns [ProcessMetric[]](structures/process-metric.md): Array of `ProcessMetric` objects that correspond to memory and cpu usage statistics of all the processes associated with the app. + +### `app.getAppMetrics()` + +Returns [ProcessMetric[]](structures/process-metric.md): Array of `ProcessMetric` objects that correspond to memory and cpu usage statistics of all the processes associated with the app. ### `app.setBadgeCount(count)` _Linux_ _macOS_ diff --git a/docs/api/structures/process-memory-info.md b/docs/api/structures/process-memory-info.md deleted file mode 100644 index 68198f2d4540..000000000000 --- a/docs/api/structures/process-memory-info.md +++ /dev/null @@ -1,4 +0,0 @@ -# ProcessMemoryInfo Object - -* `pid` Integer - Process id of the process. -* `memory` [MemoryInfo](memory-info.md) - Memory information of the process. diff --git a/docs/api/structures/process-metric.md b/docs/api/structures/process-metric.md new file mode 100644 index 000000000000..d7d476b9b441 --- /dev/null +++ b/docs/api/structures/process-metric.md @@ -0,0 +1,6 @@ +# ProcessMetric Object + +* `pid` Integer - Process id of the process. +* `type` String - Process type (Browser or Tab or GPU etc). +* `memory` [MemoryInfo](memory-info.md) - Memory information of the process. +* `cpu` [CPUUsage](cpu-usage.md) - CPU Of the process diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 1bcc1a5c6223..b73f058bdaeb 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -534,15 +534,18 @@ describe('app module', function () { }) }) - describe('getAppMemoryInfo() API', function () { + describe.only('getAppMetrics() API', function () { it('returns the process memory of all running electron processes', function () { - const appMemoryInfo = app.getAppMemoryInfo() - assert.ok(appMemoryInfo.length > 0, 'App memory info object is not > 0') - for (const {memory, pid} of appMemoryInfo) { + const appMetrics = app.getAppMetrics() + assert.ok(appMetrics.length > 0, 'App memory info object is not > 0') + for (const {memory, pid, type, cpu} of appMetrics) { assert.ok(memory.workingSetSize > 0, 'working set size is not > 0') assert.ok(memory.privateBytes > 0, 'private bytes is not > 0') assert.ok(memory.sharedBytes > 0, 'shared bytes is not > 0') assert.ok(pid > 0, 'pid is not > 0') + assert.ok(type.length > 0, 'process type is null') + assert.equal(typeof cpu.percentCPUUsage, 'number') + assert.equal(typeof cpu.idleWakeupsPerSecond, 'number') } }) }) diff --git a/vendor/brightray b/vendor/brightray new file mode 160000 index 000000000000..28d713bb2a82 --- /dev/null +++ b/vendor/brightray @@ -0,0 +1 @@ +Subproject commit 28d713bb2a82ba690a21d62522ecd7bad09caba8