| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -343,6 +343,20 @@ if (isMainThread) { | |||
| 343 | 343 | } | |
| 344 | 344 | ``` | |
| 345 | 345 | ||
| 346 | + ## `v8.setHeapSnapshotNearHeapLimit(limit)` | ||
| 347 | + | ||
| 348 | + <!-- YAML | ||
| 349 | + added: REPLACEME | ||
| 350 | + --> | ||
| 351 | + | ||
| 352 | + > Stability: 1 - Experimental | ||
| 353 | + | ||
| 354 | + * `limit` {integer} | ||
| 355 | + | ||
| 356 | + The API is a no-op if `--heapsnapshot-near-heap-limit` is already set from the | ||
| 357 | + command line or the API is called more than once. `limit` must be a positive | ||
| 358 | + integer. See [`--heapsnapshot-near-heap-limit`][] for more information. | ||
| 359 | + | ||
| 346 | 360 | ## Serialization API | |
| 347 | 361 | ||
| 348 | 362 | The serialization API provides means of serializing JavaScript values in a way | |
@@ -987,6 +1001,7 @@ Returns true if the Node.js instance is run to build a snapshot. | |||
| 987 | 1001 | [HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm | |
| 988 | 1002 | [Hook Callbacks]: #hook-callbacks | |
| 989 | 1003 | [V8]: https://developers.google.com/v8/ | |
| 1004 | + [`--heapsnapshot-near-heap-limit`]: cli.md#--heapsnapshot-near-heap-limitmax_count | ||
| 990 | 1005 | [`AsyncLocalStorage`]: async_context.md#class-asynclocalstorage | |
| 991 | 1006 | [`Buffer`]: buffer.md | |
| 992 | 1007 | [`DefaultDeserializer`]: #class-v8defaultdeserializer | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -33,7 +33,7 @@ const { | |||
| 33 | 33 | } = primordials; | |
| 34 | 34 | ||
| 35 | 35 | const { Buffer } = require('buffer'); | |
| 36 | - const { validateString } = require('internal/validators'); | ||
| 36 | + const { validateString, validateUint32 } = require('internal/validators'); | ||
| 37 | 37 | const { | |
| 38 | 38 | Serializer, | |
| 39 | 39 | Deserializer | |
@@ -59,6 +59,7 @@ const { | |||
| 59 | 59 | } = internalBinding('heap_utils'); | |
| 60 | 60 | const { HeapSnapshotStream } = require('internal/heap_utils'); | |
| 61 | 61 | const promiseHooks = require('internal/promise_hooks'); | |
| 62 | + const { getOptionValue } = require('internal/options'); | ||
| 62 | 63 | ||
| 63 | 64 | /** | |
| 64 | 65 | * Generates a snapshot of the current V8 heap | |
@@ -95,6 +96,7 @@ const { | |||
| 95 | 96 | updateHeapStatisticsBuffer, | |
| 96 | 97 | updateHeapSpaceStatisticsBuffer, | |
| 97 | 98 | updateHeapCodeStatisticsBuffer, | |
| 99 | + setHeapSnapshotNearHeapLimit: _setHeapSnapshotNearHeapLimit, | ||
| 98 | 100 | ||
| 99 | 101 | // Properties for heap statistics buffer extraction. | |
| 100 | 102 | kTotalHeapSizeIndex, | |
@@ -223,6 +225,18 @@ function getHeapCodeStatistics() { | |||
| 223 | 225 | }; | |
| 224 | 226 | } | |
| 225 | 227 | ||
| 228 | + let heapSnapshotNearHeapLimitCallbackAdded = false; | ||
| 229 | + function setHeapSnapshotNearHeapLimit(limit) { | ||
| 230 | + validateUint32(limit, 'limit', 1); | ||
| 231 | + if (heapSnapshotNearHeapLimitCallbackAdded || | ||
| 232 | + getOptionValue('--heapsnapshot-near-heap-limit') > 0 | ||
| 233 | + ) { | ||
| 234 | + return; | ||
| 235 | + } | ||
| 236 | + heapSnapshotNearHeapLimitCallbackAdded = true; | ||
| 237 | + _setHeapSnapshotNearHeapLimit(limit); | ||
| 238 | + } | ||
| 239 | + | ||
| 226 | 240 | /* V8 serialization API */ | |
| 227 | 241 | ||
| 228 | 242 | /* JS methods for the base objects */ | |
@@ -384,5 +398,6 @@ module.exports = { | |||
| 384 | 398 | serialize, | |
| 385 | 399 | writeHeapSnapshot, | |
| 386 | 400 | promiseHooks, | |
| 387 | - startupSnapshot | ||
| 401 | + startupSnapshot, | ||
| 402 | + setHeapSnapshotNearHeapLimit, | ||
| 388 | 403 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -896,6 +896,24 @@ v8::Local<v8::Context> Environment::context() const { | |||
| 896 | 896 | return PersistentToLocal::Strong(context_); | |
| 897 | 897 | } | |
| 898 | 898 | ||
| 899 | + inline void Environment::set_heap_snapshot_near_heap_limit(uint32_t limit) { | ||
| 900 | + heap_snapshot_near_heap_limit_ = limit; | ||
| 901 | + } | ||
| 902 | + | ||
| 903 | + inline void Environment::AddHeapSnapshotNearHeapLimitCallback() { | ||
| 904 | + DCHECK(!heapsnapshot_near_heap_limit_callback_added_); | ||
| 905 | + heapsnapshot_near_heap_limit_callback_added_ = true; | ||
| 906 | + isolate_->AddNearHeapLimitCallback(Environment::NearHeapLimitCallback, this); | ||
| 907 | + } | ||
| 908 | + | ||
| 909 | + inline void Environment::RemoveHeapSnapshotNearHeapLimitCallback( | ||
| 910 | + size_t heap_limit) { | ||
| 911 | + DCHECK(heapsnapshot_near_heap_limit_callback_added_); | ||
| 912 | + heapsnapshot_near_heap_limit_callback_added_ = false; | ||
| 913 | + isolate_->RemoveNearHeapLimitCallback(Environment::NearHeapLimitCallback, | ||
| 914 | + heap_limit); | ||
| 915 | + } | ||
| 916 | + | ||
| 899 | 917 | } // namespace node | |
| 900 | 918 | ||
| 901 | 919 | // These two files depend on each other. Including base_object-inl.h after this | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -770,6 +770,9 @@ Environment::Environment(IsolateData* isolate_data, | |||
| 770 | 770 | inspector_host_port_ = std::make_shared<ExclusiveAccess<HostPort>>( | |
| 771 | 771 | options_->debug_options().host_port); | |
| 772 | 772 | ||
| 773 | + heap_snapshot_near_heap_limit_ = | ||
| 774 | + static_cast<uint32_t>(options_->heap_snapshot_near_heap_limit); | ||
| 775 | + | ||
| 773 | 776 | if (!(flags_ & EnvironmentFlags::kOwnsProcessState)) { | |
| 774 | 777 | set_abort_on_uncaught_exception(false); | |
| 775 | 778 | } | |
@@ -884,9 +887,8 @@ Environment::~Environment() { | |||
| 884 | 887 | // FreeEnvironment() should have set this. | |
| 885 | 888 | CHECK(is_stopping()); | |
| 886 | 889 | ||
| 887 | - if (options_->heap_snapshot_near_heap_limit > heap_limit_snapshot_taken_) { | ||
| 888 | - isolate_->RemoveNearHeapLimitCallback(Environment::NearHeapLimitCallback, | ||
| 889 | - 0); | ||
| 890 | + if (heapsnapshot_near_heap_limit_callback_added_) { | ||
| 891 | + RemoveHeapSnapshotNearHeapLimitCallback(0); | ||
| 890 | 892 | } | |
| 891 | 893 | ||
| 892 | 894 | isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback( | |
@@ -2029,8 +2031,7 @@ size_t Environment::NearHeapLimitCallback(void* data, | |||
| 2029 | 2031 | Debug(env, | |
| 2030 | 2032 | DebugCategory::DIAGNOSTICS, | |
| 2031 | 2033 | "Not generating snapshots because it's too risky.\n"); | |
| 2032 | - env->isolate()->RemoveNearHeapLimitCallback(NearHeapLimitCallback, | ||
| 2033 | - initial_heap_limit); | ||
| 2034 | + env->RemoveHeapSnapshotNearHeapLimitCallback(initial_heap_limit); | ||
| 2034 | 2035 | // The new limit must be higher than current_heap_limit or V8 might | |
| 2035 | 2036 | // crash. | |
| 2036 | 2037 | return current_heap_limit + 1; | |
@@ -2050,17 +2051,15 @@ size_t Environment::NearHeapLimitCallback(void* data, | |||
| 2050 | 2051 | ||
| 2051 | 2052 | // Remove the callback first in case it's triggered when generating | |
| 2052 | 2053 | // the snapshot. | |
| 2053 | - env->isolate()->RemoveNearHeapLimitCallback(NearHeapLimitCallback, | ||
| 2054 | - initial_heap_limit); | ||
| 2054 | + env->RemoveHeapSnapshotNearHeapLimitCallback(initial_heap_limit); | ||
| 2055 | 2055 | ||
| 2056 | 2056 | heap::WriteSnapshot(env->isolate(), filename.c_str()); | |
| 2057 | 2057 | env->heap_limit_snapshot_taken_ += 1; | |
| 2058 | 2058 | ||
| 2059 | 2059 | // Don't take more snapshots than the number specified by | |
| 2060 | 2060 | // --heapsnapshot-near-heap-limit. | |
| 2061 | - if (env->heap_limit_snapshot_taken_ < | ||
| 2062 | - env->options_->heap_snapshot_near_heap_limit) { | ||
| 2063 | - env->isolate()->AddNearHeapLimitCallback(NearHeapLimitCallback, env); | ||
| 2061 | + if (env->heap_limit_snapshot_taken_ < env->heap_snapshot_near_heap_limit_) { | ||
| 2062 | + env->AddHeapSnapshotNearHeapLimitCallback(); | ||
| 2064 | 2063 | } | |
| 2065 | 2064 | ||
| 2066 | 2065 | FPrintF(stderr, "Wrote snapshot to %s\n", filename.c_str()); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1459,6 +1459,12 @@ class Environment : public MemoryRetainer { | |||
| 1459 | 1459 | template <typename T> | |
| 1460 | 1460 | void ForEachBindingData(T&& iterator); | |
| 1461 | 1461 | ||
| 1462 | + inline void set_heap_snapshot_near_heap_limit(uint32_t limit); | ||
| 1463 | + | ||
| 1464 | + inline void AddHeapSnapshotNearHeapLimitCallback(); | ||
| 1465 | + | ||
| 1466 | + inline void RemoveHeapSnapshotNearHeapLimitCallback(size_t heap_limit); | ||
| 1467 | + | ||
| 1462 | 1468 | private: | |
| 1463 | 1469 | inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>), | |
| 1464 | 1470 | const char* errmsg); | |
@@ -1516,7 +1522,9 @@ class Environment : public MemoryRetainer { | |||
| 1516 | 1522 | std::string exec_path_; | |
| 1517 | 1523 | ||
| 1518 | 1524 | bool is_processing_heap_limit_callback_ = false; | |
| 1519 | - int64_t heap_limit_snapshot_taken_ = 0; | ||
| 1525 | + uint32_t heap_limit_snapshot_taken_ = 0; | ||
| 1526 | + uint32_t heap_snapshot_near_heap_limit_ = 0; | ||
| 1527 | + bool heapsnapshot_near_heap_limit_callback_added_ = false; | ||
| 1520 | 1528 | ||
| 1521 | 1529 | uint32_t module_id_counter_ = 0; | |
| 1522 | 1530 | uint32_t script_id_counter_ = 0; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -282,9 +282,8 @@ static void AtomicsWaitCallback(Isolate::AtomicsWaitEvent event, | |||
| 282 | 282 | void Environment::InitializeDiagnostics() { | |
| 283 | 283 | isolate_->GetHeapProfiler()->AddBuildEmbedderGraphCallback( | |
| 284 | 284 | Environment::BuildEmbedderGraph, this); | |
| 285 | - if (options_->heap_snapshot_near_heap_limit > 0) { | ||
| 286 | - isolate_->AddNearHeapLimitCallback(Environment::NearHeapLimitCallback, | ||
| 287 | - this); | ||
| 285 | + if (heap_snapshot_near_heap_limit_ > 0) { | ||
| 286 | + AddHeapSnapshotNearHeapLimitCallback(); | ||
| 288 | 287 | } | |
| 289 | 288 | if (options_->trace_uncaught) | |
| 290 | 289 | isolate_->SetCaptureStackTraceForUncaughtExceptions(true); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -155,6 +155,15 @@ void CachedDataVersionTag(const FunctionCallbackInfo<Value>& args) { | |||
| 155 | 155 | args.GetReturnValue().Set(result); | |
| 156 | 156 | } | |
| 157 | 157 | ||
| 158 | + void SetHeapSnapshotNearHeapLimit(const FunctionCallbackInfo<Value>& args) { | ||
| 159 | + CHECK(args[0]->IsUint32()); | ||
| 160 | + Environment* env = Environment::GetCurrent(args); | ||
| 161 | + uint32_t limit = args[0].As<v8::Uint32>()->Value(); | ||
| 162 | + CHECK_GT(limit, 0); | ||
| 163 | + env->AddHeapSnapshotNearHeapLimitCallback(); | ||
| 164 | + env->set_heap_snapshot_near_heap_limit(limit); | ||
| 165 | + } | ||
| 166 | + | ||
| 158 | 167 | void UpdateHeapStatisticsBuffer(const FunctionCallbackInfo<Value>& args) { | |
| 159 | 168 | BindingData* data = Environment::GetBindingData<BindingData>(args); | |
| 160 | 169 | HeapStatistics s; | |
@@ -212,6 +221,8 @@ void Initialize(Local<Object> target, | |||
| 212 | 221 | CachedDataVersionTag); | |
| 213 | 222 | env->SetMethod( | |
| 214 | 223 | target, "updateHeapStatisticsBuffer", UpdateHeapStatisticsBuffer); | |
| 224 | + env->SetMethodNoSideEffect(target, "setHeapSnapshotNearHeapLimit", | ||
| 225 | + SetHeapSnapshotNearHeapLimit); | ||
| 215 | 226 | ||
| 216 | 227 | env->SetMethod( | |
| 217 | 228 | target, "updateHeapCodeStatisticsBuffer", UpdateHeapCodeStatisticsBuffer); | |
@@ -259,6 +270,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { | |||
| 259 | 270 | registry->Register(UpdateHeapCodeStatisticsBuffer); | |
| 260 | 271 | registry->Register(UpdateHeapSpaceStatisticsBuffer); | |
| 261 | 272 | registry->Register(SetFlagsFromString); | |
| 273 | + registry->Register(SetHeapSnapshotNearHeapLimit); | ||
| 262 | 274 | } | |
| 263 | 275 | ||
| 264 | 276 | } // namespace v8_utils | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,9 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const path = require('path'); | ||
| 3 | + const v8 = require('v8'); | ||
| 4 | + | ||
| 5 | + v8.setHeapSnapshotNearHeapLimit(+process.env.limit); | ||
| 6 | + if (process.env.limit2) { | ||
| 7 | + v8.setHeapSnapshotNearHeapLimit(+process.env.limit2); | ||
| 8 | + } | ||
| 9 | + require(path.resolve(__dirname, 'grow.js')); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,15 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const path = require('path'); | ||
| 3 | + const { Worker } = require('worker_threads'); | ||
| 4 | + const max_snapshots = parseInt(process.env.TEST_SNAPSHOTS) || 1; | ||
| 5 | + new Worker(path.join(__dirname, 'grow-and-set-near-heap-limit.js'), { | ||
| 6 | + env: { | ||
| 7 | + ...process.env, | ||
| 8 | + limit: max_snapshots, | ||
| 9 | + }, | ||
| 10 | + resourceLimits: { | ||
| 11 | + maxOldGenerationSizeMb: | ||
| 12 | + parseInt(process.env.TEST_OLD_SPACE_SIZE) || 20 | ||
| 13 | + } | ||
| 14 | + }); | ||
| 15 | + | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,41 @@ | |||
| 1 | + // Copy from test-heapsnapshot-near-heap-limit-worker.js | ||
| 2 | + 'use strict'; | ||
| 3 | + | ||
| 4 | + require('../common'); | ||
| 5 | + const tmpdir = require('../common/tmpdir'); | ||
| 6 | + const assert = require('assert'); | ||
| 7 | + const { spawnSync } = require('child_process'); | ||
| 8 | + const fixtures = require('../common/fixtures'); | ||
| 9 | + const fs = require('fs'); | ||
| 10 | + | ||
| 11 | + const env = { | ||
| 12 | + ...process.env, | ||
| 13 | + NODE_DEBUG_NATIVE: 'diagnostics' | ||
| 14 | + }; | ||
| 15 | + | ||
| 16 | + { | ||
| 17 | + tmpdir.refresh(); | ||
| 18 | + const child = spawnSync(process.execPath, [ | ||
| 19 | + fixtures.path('workload', 'grow-worker-and-set-near-heap-limit.js'), | ||
| 20 | + ], { | ||
| 21 | + cwd: tmpdir.path, | ||
| 22 | + env: { | ||
| 23 | + TEST_SNAPSHOTS: 1, | ||
| 24 | + TEST_OLD_SPACE_SIZE: 50, | ||
| 25 | + ...env | ||
| 26 | + } | ||
| 27 | + }); | ||
| 28 | + console.log(child.stdout.toString()); | ||
| 29 | + const stderr = child.stderr.toString(); | ||
| 30 | + console.log(stderr); | ||
| 31 | + const risky = /Not generating snapshots because it's too risky/.test(stderr); | ||
| 32 | + if (!risky) { | ||
| 33 | + // There should be one snapshot taken and then after the | ||
| 34 | + // snapshot heap limit callback is popped, the OOM callback | ||
| 35 | + // becomes effective. | ||
| 36 | + assert(stderr.includes('ERR_WORKER_OUT_OF_MEMORY')); | ||
| 37 | + const list = fs.readdirSync(tmpdir.path) | ||
| 38 | + .filter((file) => file.endsWith('.heapsnapshot')); | ||
| 39 | + assert.strictEqual(list.length, 1); | ||
| 40 | + } | ||
| 41 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments