fix: support for v8.setHeapSnapshotNearHeapLimit api (#45606)
* fix: support for v8.setHeapSnapshotNearHeapLimit api * docs: add support
This commit is contained in:
parent
a841d6484c
commit
137a552641
10 changed files with 66 additions and 6 deletions
|
@ -313,6 +313,12 @@ Set the default value of the `verbatim` parameter in the Node.js [`dns.lookup()`
|
||||||
|
|
||||||
The default is `verbatim` and `dns.setDefaultResultOrder()` have higher priority than `--dns-result-order`.
|
The default is `verbatim` and `dns.setDefaultResultOrder()` have higher priority than `--dns-result-order`.
|
||||||
|
|
||||||
|
### `--diagnostic-dir=directory`
|
||||||
|
|
||||||
|
Set the directory to which all Node.js diagnostic output files are written. Defaults to current working directory.
|
||||||
|
|
||||||
|
Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https://nodejs.org/docs/latest/api/v8.html#v8setheapsnapshotnearheaplimitlimit).
|
||||||
|
|
||||||
[app]: app.md
|
[app]: app.md
|
||||||
[append-switch]: command-line.md#commandlineappendswitchswitch-value
|
[append-switch]: command-line.md#commandlineappendswitchswitch-value
|
||||||
[debugging-main-process]: ../tutorial/debugging-main-process.md
|
[debugging-main-process]: ../tutorial/debugging-main-process.md
|
||||||
|
|
|
@ -237,7 +237,8 @@ void ElectronBrowserMainParts::PostEarlyInitialization() {
|
||||||
node_bindings_->Initialize(js_env_->isolate()->GetCurrentContext());
|
node_bindings_->Initialize(js_env_->isolate()->GetCurrentContext());
|
||||||
// Create the global environment.
|
// Create the global environment.
|
||||||
node_env_ = node_bindings_->CreateEnvironment(
|
node_env_ = node_bindings_->CreateEnvironment(
|
||||||
js_env_->isolate()->GetCurrentContext(), js_env_->platform());
|
js_env_->isolate()->GetCurrentContext(), js_env_->platform(),
|
||||||
|
js_env_->max_young_generation_size_in_bytes());
|
||||||
|
|
||||||
node_env_->set_trace_sync_io(node_env_->options()->trace_sync_io);
|
node_env_->set_trace_sync_io(node_env_->options()->trace_sync_io);
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,15 @@ namespace electron {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
gin::IsolateHolder CreateIsolateHolder(v8::Isolate* isolate) {
|
gin::IsolateHolder CreateIsolateHolder(v8::Isolate* isolate,
|
||||||
|
size_t* max_young_generation_size) {
|
||||||
std::unique_ptr<v8::Isolate::CreateParams> create_params =
|
std::unique_ptr<v8::Isolate::CreateParams> create_params =
|
||||||
gin::IsolateHolder::getDefaultIsolateParams();
|
gin::IsolateHolder::getDefaultIsolateParams();
|
||||||
|
// The value is needed to adjust heap limit when capturing
|
||||||
|
// snapshot via v8.setHeapSnapshotNearHeapLimit(limit) or
|
||||||
|
// --heapsnapshot-near-heap-limit=max_count.
|
||||||
|
*max_young_generation_size =
|
||||||
|
create_params->constraints.max_young_generation_size_in_bytes();
|
||||||
// Align behavior with V8 Isolate default for Node.js.
|
// Align behavior with V8 Isolate default for Node.js.
|
||||||
// This is necessary for important aspects of Node.js
|
// This is necessary for important aspects of Node.js
|
||||||
// including heap and cpu profilers to function properly.
|
// including heap and cpu profilers to function properly.
|
||||||
|
@ -55,7 +61,8 @@ gin::IsolateHolder CreateIsolateHolder(v8::Isolate* isolate) {
|
||||||
JavascriptEnvironment::JavascriptEnvironment(uv_loop_t* event_loop,
|
JavascriptEnvironment::JavascriptEnvironment(uv_loop_t* event_loop,
|
||||||
bool setup_wasm_streaming)
|
bool setup_wasm_streaming)
|
||||||
: isolate_holder_{CreateIsolateHolder(
|
: isolate_holder_{CreateIsolateHolder(
|
||||||
Initialize(event_loop, setup_wasm_streaming))},
|
Initialize(event_loop, setup_wasm_streaming),
|
||||||
|
&max_young_generation_size_)},
|
||||||
isolate_{isolate_holder_.isolate()},
|
isolate_{isolate_holder_.isolate()},
|
||||||
locker_{isolate_} {
|
locker_{isolate_} {
|
||||||
isolate_->Enter();
|
isolate_->Enter();
|
||||||
|
|
|
@ -36,6 +36,9 @@ class JavascriptEnvironment {
|
||||||
|
|
||||||
node::MultiIsolatePlatform* platform() const { return platform_.get(); }
|
node::MultiIsolatePlatform* platform() const { return platform_.get(); }
|
||||||
v8::Isolate* isolate() const { return isolate_; }
|
v8::Isolate* isolate() const { return isolate_; }
|
||||||
|
size_t max_young_generation_size_in_bytes() const {
|
||||||
|
return max_young_generation_size_;
|
||||||
|
}
|
||||||
|
|
||||||
static v8::Isolate* GetIsolate();
|
static v8::Isolate* GetIsolate();
|
||||||
|
|
||||||
|
@ -43,6 +46,7 @@ class JavascriptEnvironment {
|
||||||
v8::Isolate* Initialize(uv_loop_t* event_loop, bool setup_wasm_streaming);
|
v8::Isolate* Initialize(uv_loop_t* event_loop, bool setup_wasm_streaming);
|
||||||
std::unique_ptr<node::MultiIsolatePlatform> platform_;
|
std::unique_ptr<node::MultiIsolatePlatform> platform_;
|
||||||
|
|
||||||
|
size_t max_young_generation_size_ = 0;
|
||||||
gin::IsolateHolder isolate_holder_;
|
gin::IsolateHolder isolate_holder_;
|
||||||
|
|
||||||
// owned-by: isolate_holder_
|
// owned-by: isolate_holder_
|
||||||
|
|
|
@ -324,6 +324,7 @@ bool IsAllowedOption(const std::string_view option) {
|
||||||
|
|
||||||
// This should be aligned with what's possible to set via the process object.
|
// This should be aligned with what's possible to set via the process object.
|
||||||
static constexpr auto options = base::MakeFixedFlatSet<std::string_view>({
|
static constexpr auto options = base::MakeFixedFlatSet<std::string_view>({
|
||||||
|
"--diagnostic-dir",
|
||||||
"--dns-result-order",
|
"--dns-result-order",
|
||||||
"--no-deprecation",
|
"--no-deprecation",
|
||||||
"--throw-deprecation",
|
"--throw-deprecation",
|
||||||
|
@ -596,6 +597,7 @@ void NodeBindings::Initialize(v8::Local<v8::Context> context) {
|
||||||
std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
|
std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
node::MultiIsolatePlatform* platform,
|
node::MultiIsolatePlatform* platform,
|
||||||
|
size_t max_young_generation_size,
|
||||||
std::vector<std::string> args,
|
std::vector<std::string> args,
|
||||||
std::vector<std::string> exec_args,
|
std::vector<std::string> exec_args,
|
||||||
std::optional<base::RepeatingCallback<void()>> on_app_code_ready) {
|
std::optional<base::RepeatingCallback<void()>> on_app_code_ready) {
|
||||||
|
@ -646,6 +648,7 @@ std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
|
||||||
args.insert(args.begin() + 1, init_script);
|
args.insert(args.begin() + 1, init_script);
|
||||||
|
|
||||||
auto* isolate_data = node::CreateIsolateData(isolate, uv_loop_, platform);
|
auto* isolate_data = node::CreateIsolateData(isolate, uv_loop_, platform);
|
||||||
|
isolate_data->max_young_gen_size = max_young_generation_size;
|
||||||
context->SetAlignedPointerInEmbedderData(kElectronContextEmbedderDataIndex,
|
context->SetAlignedPointerInEmbedderData(kElectronContextEmbedderDataIndex,
|
||||||
static_cast<void*>(isolate_data));
|
static_cast<void*>(isolate_data));
|
||||||
|
|
||||||
|
@ -783,8 +786,10 @@ std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
|
||||||
std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
|
std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
node::MultiIsolatePlatform* platform,
|
node::MultiIsolatePlatform* platform,
|
||||||
|
size_t max_young_generation_size,
|
||||||
std::optional<base::RepeatingCallback<void()>> on_app_code_ready) {
|
std::optional<base::RepeatingCallback<void()>> on_app_code_ready) {
|
||||||
return CreateEnvironment(context, platform, ElectronCommandLine::AsUtf8(), {},
|
return CreateEnvironment(context, platform, max_young_generation_size,
|
||||||
|
ElectronCommandLine::AsUtf8(), {},
|
||||||
on_app_code_ready);
|
on_app_code_ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,7 @@ class NodeBindings {
|
||||||
std::shared_ptr<node::Environment> CreateEnvironment(
|
std::shared_ptr<node::Environment> CreateEnvironment(
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
node::MultiIsolatePlatform* platform,
|
node::MultiIsolatePlatform* platform,
|
||||||
|
size_t max_young_generation_size,
|
||||||
std::vector<std::string> args,
|
std::vector<std::string> args,
|
||||||
std::vector<std::string> exec_args,
|
std::vector<std::string> exec_args,
|
||||||
std::optional<base::RepeatingCallback<void()>> on_app_code_ready =
|
std::optional<base::RepeatingCallback<void()>> on_app_code_ready =
|
||||||
|
@ -141,6 +142,7 @@ class NodeBindings {
|
||||||
std::shared_ptr<node::Environment> CreateEnvironment(
|
std::shared_ptr<node::Environment> CreateEnvironment(
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
node::MultiIsolatePlatform* platform,
|
node::MultiIsolatePlatform* platform,
|
||||||
|
size_t max_young_generation_size = 0,
|
||||||
std::optional<base::RepeatingCallback<void()>> on_app_code_ready =
|
std::optional<base::RepeatingCallback<void()>> on_app_code_ready =
|
||||||
std::nullopt);
|
std::nullopt);
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ void ElectronRendererClient::DidCreateScriptContext(
|
||||||
blink::LoaderFreezeMode::kStrict);
|
blink::LoaderFreezeMode::kStrict);
|
||||||
|
|
||||||
std::shared_ptr<node::Environment> env = node_bindings_->CreateEnvironment(
|
std::shared_ptr<node::Environment> env = node_bindings_->CreateEnvironment(
|
||||||
renderer_context, nullptr,
|
renderer_context, nullptr, 0,
|
||||||
base::BindRepeating(&ElectronRendererClient::UndeferLoad,
|
base::BindRepeating(&ElectronRendererClient::UndeferLoad,
|
||||||
base::Unretained(this), render_frame));
|
base::Unretained(this), render_frame));
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,8 @@ void NodeService::Initialize(
|
||||||
// Create the global environment.
|
// Create the global environment.
|
||||||
node_env_ = node_bindings_->CreateEnvironment(
|
node_env_ = node_bindings_->CreateEnvironment(
|
||||||
js_env_->isolate()->GetCurrentContext(), js_env_->platform(),
|
js_env_->isolate()->GetCurrentContext(), js_env_->platform(),
|
||||||
params->args, params->exec_args);
|
js_env_->max_young_generation_size_in_bytes(), params->args,
|
||||||
|
params->exec_args);
|
||||||
|
|
||||||
// Override the default handler set by NodeBindings.
|
// Override the default handler set by NodeBindings.
|
||||||
node_env_->isolate()->SetFatalErrorHandler(V8FatalErrorCallback);
|
node_env_->isolate()->SetFatalErrorHandler(V8FatalErrorCallback);
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { expect } from 'chai';
|
||||||
|
|
||||||
import * as childProcess from 'node:child_process';
|
import * as childProcess from 'node:child_process';
|
||||||
import { once } from 'node:events';
|
import { once } from 'node:events';
|
||||||
|
import * as fs from 'node:fs/promises';
|
||||||
|
import * as os from 'node:os';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { setImmediate } from 'node:timers/promises';
|
import { setImmediate } from 'node:timers/promises';
|
||||||
import { pathToFileURL } from 'node:url';
|
import { pathToFileURL } from 'node:url';
|
||||||
|
@ -760,5 +762,26 @@ describe('utilityProcess module', () => {
|
||||||
expect(loginAuthInfo!.realm).to.equal('Foo');
|
expect(loginAuthInfo!.realm).to.equal('Foo');
|
||||||
expect(loginAuthInfo!.scheme).to.equal('basic');
|
expect(loginAuthInfo!.scheme).to.equal('basic');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports generating snapshots via v8.setHeapSnapshotNearHeapLimit', async () => {
|
||||||
|
const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-spec-utility-oom-'));
|
||||||
|
const child = utilityProcess.fork(path.join(fixturesPath, 'oom-grow.js'), [], {
|
||||||
|
stdio: 'ignore',
|
||||||
|
execArgv: [
|
||||||
|
`--diagnostic-dir=${tmpDir}`,
|
||||||
|
'--js-flags=--max-old-space-size=50'
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
NODE_DEBUG_NATIVE: 'diagnostic'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await once(child, 'spawn');
|
||||||
|
await once(child, 'exit');
|
||||||
|
const files = (await fs.readdir(tmpDir)).filter((file) => file.endsWith('.heapsnapshot'));
|
||||||
|
expect(files.length).to.be.equal(1);
|
||||||
|
const stat = await fs.stat(path.join(tmpDir, files[0]));
|
||||||
|
expect(stat.size).to.be.greaterThan(0);
|
||||||
|
await fs.rm(tmpDir, { recursive: true });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
11
spec/fixtures/api/utility-process/oom-grow.js
vendored
Normal file
11
spec/fixtures/api/utility-process/oom-grow.js
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
const v8 = require('node:v8');
|
||||||
|
|
||||||
|
v8.setHeapSnapshotNearHeapLimit(1);
|
||||||
|
|
||||||
|
const arr = [];
|
||||||
|
function runAllocation () {
|
||||||
|
const str = JSON.stringify(process.config).slice(0, 1000);
|
||||||
|
arr.push(str);
|
||||||
|
setImmediate(runAllocation);
|
||||||
|
}
|
||||||
|
setImmediate(runAllocation);
|
Loading…
Add table
Add a link
Reference in a new issue