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: Implement process.getProcessMemoryInfo to get the process memory usage #14847
Conversation
#include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h" | ||
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" | ||
// Must be the last in the includes list. | ||
#include "atom/common/node_includes.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure that clang-format
won't "fix" the order of includes here.
Usually leaving an empty line before such an include helps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the comment helps, and prevents sorting. But will verify, thanks.
spec/api-process-spec.js
Outdated
// }) | ||
describe('process.getMemoryFootprint()', () => { | ||
it('resolves promise successfully with non zero value', (done) => { | ||
process.getMemoryFootprint().then((memoryFootprint) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please use async / await
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still WIP, but yes. 👍
The crash should have been fixed now. |
atom/common/api/atom_bindings.cc
Outdated
#include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h" | ||
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" | ||
|
||
// Must be the last in the includes list. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence should end in because ${reason}
atom/common/api/atom_bindings.cc
Outdated
} | ||
if (!resolved) { | ||
promise->RejectWithErrorMessage( | ||
R"(Failed to find current process memory details in memory dump)"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
Why is the
R"
prefix needed -- there aren't any escaped characters in the string? -
Why is the error message in parenthesis?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a long sentence, so I think the linter suggested I do this.
The parentheses are part of the syntax for raw string literals, and will not be in the error message; but I could be wrong on this one.
atom/common/api/atom_bindings.cc
Outdated
promise->RejectWithErrorMessage( | ||
R"(Failed to find current process memory details in memory dump)"); | ||
} | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be slightly cleaner to avoid unnecessary nesting by getting the error condition out of the way first:
if (!success) {
promise->RejectWithErrorMessage("...");
return;
}
bool resolved = false;
for (...
to avoid unnecessary nesting
let memoryFootprint = await process.getMemoryFootprint() | ||
expect(memoryFootprint).to.be.a('number').greaterThan(0) | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a writeup in docs/api/process.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, mainly waiting for reviews on the API itself before writing the documentation.
atom/common/api/atom_bindings.cc
Outdated
for (const memory_instrumentation::GlobalMemoryDump::ProcessDump& dump : | ||
global_dump->process_dumps()) { | ||
if (base::GetCurrentProcId() == dump.pid()) { | ||
promise->Resolve(dump.os_dump().private_footprint_kb); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like os_dump()'s return has both private_footprint_kb and shared_footprint_kb. That's is pretty similar to the MemoryInfo API we were using before, which had privateBytes and sharedBytes.
Would it make sense to keep using MemoryInfo as a return value and store both values in it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, it would be great to expose more than just a single number here. It looks like the os_dump
also has resident_set_kb
, which would be nice to expose. It looks like you can also request specific allocator dumps from the memory instrumentation code (and starting in M69, the list of dumps to request is becoming non-optional: https://chromium.googlesource.com/chromium/src/+/69.0.3497.106/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h#78). It might be cool to expose those too? Though i'm not entirely sure what sort of metrics you can request dumps of.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be used as a new backend for app.getAppMetrics()
, which was in 3.0 but removed in master due to the backend API disappearing?
And, does it make sense to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea. 👍
Do we want to keep the same api also, getProcessMemoryInfo
? It will have to return a promise though.
What about working set size and peak working set size? The original API returned their values on all platforms, but those terms have meaning only on windows.
Please ignore, didn't see the above two messages before responding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I definitely want to add resident_set_kb
and shared_footprint_kb
to the same API. So. we can probably leave the same getProcessMemoryInfo
API and return an updated MemoryInfo
struct containing { resident_set_kb, shared_footprint_kb, private_footprint_kb}
.
resident_set_kb
is mostly a better replacement for the existing working set size attributes.
Regarding requesting specific allocator dumps, I'm not sure if they should be part of this process
API. They can be part of the app.getAppMetrics
api though.
atom/common/api/atom_bindings.cc
Outdated
scoped_refptr<util::Promise> promise = new util::Promise(isolate); | ||
memory_instrumentation::MemoryInstrumentation::GetInstance() | ||
->RequestGlobalDump(std::vector<std::string>(), | ||
base::AdaptCallbackForRepeating(base::BindOnce( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base::AdaptCallbackForRepeating
is deprecated and not advised. Can we use BindRepeating
here (or justify the use of AdaptCallbackForRepeating
?
atom/common/api/atom_bindings.cc
Outdated
for (const memory_instrumentation::GlobalMemoryDump::ProcessDump& dump : | ||
global_dump->process_dumps()) { | ||
if (base::GetCurrentProcId() == dump.pid()) { | ||
promise->Resolve(dump.os_dump().private_footprint_kb); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, it would be great to expose more than just a single number here. It looks like the os_dump
also has resident_set_kb
, which would be nice to expose. It looks like you can also request specific allocator dumps from the memory instrumentation code (and starting in M69, the list of dumps to request is becoming non-optional: https://chromium.googlesource.com/chromium/src/+/69.0.3497.106/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h#78). It might be cool to expose those too? Though i'm not entirely sure what sort of metrics you can request dumps of.
atom/common/api/atom_bindings.cc
Outdated
#if defined(OS_LINUX) | ||
dict.Set("residentSetBytes", osdump.resident_set_kb); | ||
#elif defined(OS_WIN) | ||
dict.Set("workingSetSize", osdump.resident_set_kb); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why name this differently on different OSes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to do that because resident set size is called working set size on windows.
Also, internally it calls the similarly named windows API which does that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the concepts are similar enough to share the name "residentSetBytes" (though it looks like it would actually be residentSetKilobytes
from the variable name resident_set_kb
?). Chromium calls them the same thing.
Refs:
https://docs.microsoft.com/en-us/windows/desktop/psapi/process-memory-usage-information
The working set is the amount of memory physically mapped to the process context at a given time.
https://docs.microsoft.com/en-us/windows/desktop/procthread/process-working-set
The working set of a program is a collection of those pages in its virtual address space that have been recently referenced. It includes both shared and private data.
https://stackoverflow.com/a/21049737
RSS is the Resident Set Size and is used to show how much memory is allocated to that process and is in RAM. It does not include memory that is swapped out. It does include memory from shared libraries as long as the pages from those libraries are actually in memory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just docs questions now I think
docs/api/process.md
Outdated
* `private` Integer - The amount of memory not shared by other processes, such as | ||
JS heap or HTML content. | ||
* `shared` Integer - The amount of memory shared between processes, typically | ||
memory consumed by the Electron code itself. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to add (redundant) information on each of these property descriptions saying they're in KB, the note below is easy to miss.
docs/api/process.md
Outdated
Returns `Object`: | ||
|
||
* `residentSet` Integer _Linux_ and _Windows_ - The amount of memory | ||
currently pinned to actual physical RAM. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expect we'll get questions about why this metric isn't available on mac. Do you have any references you could add here about what a comparable statistic might be on macOS, or why this number isn't available on mac?
Co-Authored-By: nitsakh <nitsakh@icloud.com>
Co-Authored-By: nitsakh <nitsakh@icloud.com>
Co-Authored-By: nitsakh <nitsakh@icloud.com>
Co-Authored-By: nitsakh <nitsakh@icloud.com>
Electron crashed when running the tests:
|
Release Notes Persisted
|
/trop run backport |
The backport process for this PR has been manually initiated, here we go! :D |
I was unable to backport this PR to "4-0-x" cleanly; |
@alexeykuzmin is this not being considered for a backport to 4.0.x? |
…y usage (#14847) * feat: Implement process.getMemoryFootprint to get the process memory usage * Add spec * fix: must enter node env in callback * Update function call * Update spec * Update API data * update spec * Update include * update test for shared bytes * Update atom/common/api/atom_bindings.cc Co-Authored-By: nitsakh <nitsakh@icloud.com> * Update atom/common/api/atom_bindings.cc Co-Authored-By: nitsakh <nitsakh@icloud.com> * Update API * Update the callback isolate * Update to work after app ready * Update docs * Update docs/api/process.md Co-Authored-By: nitsakh <nitsakh@icloud.com> * Update docs/api/process.md Co-Authored-By: nitsakh <nitsakh@icloud.com> * Fix crash
…y usage (#14847) * feat: Implement process.getMemoryFootprint to get the process memory usage * Add spec * fix: must enter node env in callback * Update function call * Update spec * Update API data * update spec * Update include * update test for shared bytes * Update atom/common/api/atom_bindings.cc Co-Authored-By: nitsakh <nitsakh@icloud.com> * Update atom/common/api/atom_bindings.cc Co-Authored-By: nitsakh <nitsakh@icloud.com> * Update API * Update the callback isolate * Update to work after app ready * Update docs * Update docs/api/process.md Co-Authored-By: nitsakh <nitsakh@icloud.com> * Update docs/api/process.md Co-Authored-By: nitsakh <nitsakh@icloud.com> * Fix crash
@alexeykuzmin great to hear |
…y usage (#14847) (#16591) * feat: Implement process.getMemoryFootprint to get the process memory usage * Add spec * fix: must enter node env in callback * Update function call * Update spec * Update API data * update spec * Update include * update test for shared bytes * Update atom/common/api/atom_bindings.cc Co-Authored-By: nitsakh <nitsakh@icloud.com> * Update atom/common/api/atom_bindings.cc Co-Authored-By: nitsakh <nitsakh@icloud.com> * Update API * Update the callback isolate * Update to work after app ready * Update docs * Update docs/api/process.md Co-Authored-By: nitsakh <nitsakh@icloud.com> * Update docs/api/process.md Co-Authored-By: nitsakh <nitsakh@icloud.com> * Fix crash
Description of Change
This implements the memory footprint API.
Since version 67, chromium has moved to using memory footprint internally as the cross platform memory indicator. They got rid of other various cross platform APIs that mean different things on different platforms #13447.
Checklist
npm test
passesRelease Notes
Notes: Add getMemoryFootprint API