mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
v8: add setHeapProfileNearHeapLimit
Signed-off-by: ishabi <ilyasshabi94@gmail.com> PR-URL: https://github.com/nodejs/node/pull/64676 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
1 parent
0375d09352
commit
5ed55ba0b8
15 files changed
+361
-6
No files matched your search
@@ -1853,6 +1853,39 @@ const profile = handle.stop();
|
||||
console.log(profile);
|
||||
```
|
||||
|
||||
## `v8.setHeapProfileNearHeapLimit(limit)`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1 - Experimental
|
||||
|
||||
* `limit` {integer}
|
||||
|
||||
Writes the active sampling heap profile as a `.heapprofile` file when V8 is
|
||||
near the heap limit, up to `limit` times. Files are written to the directory
|
||||
configured by `--diagnostic-dir` (or to the current working directory if the
|
||||
option is unset), using the
|
||||
`Heap.${yyyymmdd}.${hhmmss}.${pid}.${tid}.${seq}.heapprofile` naming
|
||||
convention.
|
||||
|
||||
[`v8.startHeapProfile()`][] must be called before this API can capture a
|
||||
profile. If no sampling heap profile is active when V8 reaches the heap
|
||||
limit, the callback is uninstalled and V8 continues its normal
|
||||
out-of-memory behavior.
|
||||
|
||||
This API can be used together with [`v8.setHeapSnapshotNearHeapLimit()`][].
|
||||
Each API maintains its own `limit`.
|
||||
|
||||
Writing is best-effort. Materializing the sampled call tree can allocate V8
|
||||
and native memory, so the profile might not be completed if the process has
|
||||
insufficient memory available. The serialized profile is written by native code
|
||||
and is not exposed to JavaScript.
|
||||
|
||||
Calling `setHeapProfileNearHeapLimit()` more than once is a no-op. `limit`
|
||||
must be a positive integer.
|
||||
|
||||
[CppHeap]: https://v8docs.nodesource.com/node-22.4/d9/dc4/classv8_1_1_cpp_heap.html
|
||||
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
[Hook Callbacks]: #hook-callbacks
|
||||
@@ -1887,6 +1920,8 @@ console.log(profile);
|
||||
[`serializer.transferArrayBuffer()`]: #serializertransferarraybufferid-arraybuffer
|
||||
[`serializer.writeRawBytes()`]: #serializerwriterawbytesbuffer
|
||||
[`settled` callback]: #settledpromise
|
||||
[`v8.setHeapSnapshotNearHeapLimit()`]: #v8setheapsnapshotnearheaplimitlimit
|
||||
[`v8.startHeapProfile()`]: #v8startheapprofileoptions
|
||||
[`v8.stopCoverage()`]: #v8stopcoverage
|
||||
[`v8.takeCoverage()`]: #v8takecoverage
|
||||
[`vm.Script`]: vm.md#new-vmscriptcode-options
|
||||
|
||||
@@ -128,6 +128,7 @@ const {
|
||||
updateHeapSpaceStatisticsBuffer,
|
||||
updateHeapCodeStatisticsBuffer,
|
||||
setHeapSnapshotNearHeapLimit: _setHeapSnapshotNearHeapLimit,
|
||||
setHeapProfileNearHeapLimit: _setHeapProfileNearHeapLimit,
|
||||
|
||||
// Properties for heap statistics buffer extraction.
|
||||
kTotalHeapSizeIndex,
|
||||
@@ -360,6 +361,16 @@ function setHeapSnapshotNearHeapLimit(limit) {
|
||||
_setHeapSnapshotNearHeapLimit(limit);
|
||||
}
|
||||
|
||||
let heapProfileNearHeapLimitAdded = false;
|
||||
function setHeapProfileNearHeapLimit(limit) {
|
||||
validateUint32(limit, 'limit', true);
|
||||
if (heapProfileNearHeapLimitAdded) {
|
||||
return;
|
||||
}
|
||||
_setHeapProfileNearHeapLimit(limit);
|
||||
heapProfileNearHeapLimitAdded = true;
|
||||
}
|
||||
|
||||
const detailLevelDict = {
|
||||
__proto__: null,
|
||||
detailed: detailLevel.DETAILED,
|
||||
@@ -563,6 +574,7 @@ module.exports = {
|
||||
queryObjects,
|
||||
startupSnapshot,
|
||||
setHeapSnapshotNearHeapLimit,
|
||||
setHeapProfileNearHeapLimit,
|
||||
GCProfiler,
|
||||
isStringOneByteRepresentation,
|
||||
startCpuProfile,
|
||||
|
||||
+37
-3
@@ -923,26 +923,60 @@ inline void Environment::set_heap_snapshot_near_heap_limit(uint32_t limit) {
|
||||
heap_snapshot_near_heap_limit_ = limit;
|
||||
}
|
||||
|
||||
inline void Environment::set_heap_profile_near_heap_limit(uint32_t limit) {
|
||||
heap_profile_near_heap_limit_ = limit;
|
||||
}
|
||||
|
||||
inline bool Environment::is_in_heapsnapshot_heap_limit_callback() const {
|
||||
return is_in_heapsnapshot_heap_limit_callback_;
|
||||
}
|
||||
|
||||
inline bool Environment::is_in_heap_profile_near_heap_limit_callback() const {
|
||||
return is_in_heap_profile_near_heap_limit_callback_;
|
||||
}
|
||||
|
||||
inline bool Environment::report_exclude_env() const {
|
||||
return options_->report_exclude_env;
|
||||
}
|
||||
|
||||
inline void Environment::AddHeapSnapshotNearHeapLimitCallback() {
|
||||
DCHECK(!heapsnapshot_near_heap_limit_callback_added_);
|
||||
const bool was_registered = heap_profile_near_heap_limit_callback_added_;
|
||||
heapsnapshot_near_heap_limit_callback_added_ = true;
|
||||
isolate_->AddNearHeapLimitCallback(Environment::NearHeapLimitCallback, this);
|
||||
if (!was_registered) {
|
||||
isolate_->AddNearHeapLimitCallback(Environment::NearHeapLimitCallback,
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Environment::RemoveHeapSnapshotNearHeapLimitCallback(
|
||||
size_t heap_limit) {
|
||||
DCHECK(heapsnapshot_near_heap_limit_callback_added_);
|
||||
heapsnapshot_near_heap_limit_callback_added_ = false;
|
||||
isolate_->RemoveNearHeapLimitCallback(Environment::NearHeapLimitCallback,
|
||||
heap_limit);
|
||||
if (!heap_profile_near_heap_limit_callback_added_) {
|
||||
isolate_->RemoveNearHeapLimitCallback(Environment::NearHeapLimitCallback,
|
||||
heap_limit);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Environment::AddHeapProfileNearHeapLimitCallback() {
|
||||
DCHECK(!heap_profile_near_heap_limit_callback_added_);
|
||||
const bool was_registered = heapsnapshot_near_heap_limit_callback_added_;
|
||||
heap_profile_near_heap_limit_callback_added_ = true;
|
||||
if (!was_registered) {
|
||||
isolate_->AddNearHeapLimitCallback(Environment::NearHeapLimitCallback,
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Environment::RemoveHeapProfileNearHeapLimitCallback(
|
||||
size_t heap_limit) {
|
||||
DCHECK(heap_profile_near_heap_limit_callback_added_);
|
||||
heap_profile_near_heap_limit_callback_added_ = false;
|
||||
if (!heapsnapshot_near_heap_limit_callback_added_) {
|
||||
isolate_->RemoveNearHeapLimitCallback(Environment::NearHeapLimitCallback,
|
||||
heap_limit);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
+121
-1
@@ -9,9 +9,11 @@
|
||||
#include "node_context_data.h"
|
||||
#include "node_contextify.h"
|
||||
#include "node_errors.h"
|
||||
#include "node_file_utils.h"
|
||||
#include "node_internals.h"
|
||||
#include "node_options-inl.h"
|
||||
#include "node_process-inl.h"
|
||||
#include "node_profiling.h"
|
||||
#include "node_shadow_realm.h"
|
||||
#include "node_snapshotable.h"
|
||||
#include "node_v8_platform-inl.h"
|
||||
@@ -36,6 +38,7 @@
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace node {
|
||||
@@ -1112,6 +1115,9 @@ Environment::~Environment() {
|
||||
if (heapsnapshot_near_heap_limit_callback_added_) {
|
||||
RemoveHeapSnapshotNearHeapLimitCallback(0);
|
||||
}
|
||||
if (heap_profile_near_heap_limit_callback_added_) {
|
||||
RemoveHeapProfileNearHeapLimitCallback(0);
|
||||
}
|
||||
|
||||
isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback(
|
||||
BuildEmbedderGraph, this);
|
||||
@@ -2179,14 +2185,49 @@ void Environment::TracePromises(PromiseHookType type,
|
||||
PrintCurrentStackTrace(isolate);
|
||||
}
|
||||
|
||||
// V8 only invokes the most recently registered near-heap-limit callback.
|
||||
// To allow the snapshot and profile handlers to coexist on the same
|
||||
// isolate, both register through this single V8-facing entry point. It fans
|
||||
// out to whichever sub-handlers are active and returns the largest requested
|
||||
// heap limit.
|
||||
size_t Environment::NearHeapLimitCallback(void* data,
|
||||
size_t current_heap_limit,
|
||||
size_t initial_heap_limit) {
|
||||
auto* env = static_cast<Environment*>(data);
|
||||
|
||||
// Profile and snapshot generation can allocate and invoke this callback
|
||||
// recursively. Only extend the heap for the operation already in progress;
|
||||
// do not start the other diagnostic from the nested invocation.
|
||||
if (env->is_in_heapsnapshot_heap_limit_callback_) {
|
||||
return HeapSnapshotNearHeapLimitCallback(
|
||||
data, current_heap_limit, initial_heap_limit);
|
||||
}
|
||||
if (env->is_in_heap_profile_near_heap_limit_callback_) {
|
||||
return HeapProfileNearHeapLimitCallback(
|
||||
data, current_heap_limit, initial_heap_limit);
|
||||
}
|
||||
|
||||
size_t new_limit = current_heap_limit;
|
||||
if (env->heapsnapshot_near_heap_limit_callback_added_) {
|
||||
new_limit = std::max(new_limit,
|
||||
HeapSnapshotNearHeapLimitCallback(
|
||||
data, current_heap_limit, initial_heap_limit));
|
||||
}
|
||||
if (env->heap_profile_near_heap_limit_callback_added_) {
|
||||
new_limit = std::max(new_limit,
|
||||
HeapProfileNearHeapLimitCallback(
|
||||
data, current_heap_limit, initial_heap_limit));
|
||||
}
|
||||
return new_limit;
|
||||
}
|
||||
|
||||
size_t Environment::HeapSnapshotNearHeapLimitCallback(
|
||||
void* data, size_t current_heap_limit, size_t initial_heap_limit) {
|
||||
auto* env = static_cast<Environment*>(data);
|
||||
|
||||
Debug(env,
|
||||
DebugCategory::DIAGNOSTICS,
|
||||
"Invoked NearHeapLimitCallback, processing=%d, "
|
||||
"Invoked HeapSnapshotNearHeapLimitCallback, processing=%d, "
|
||||
"current_limit=%" PRIu64 ", "
|
||||
"initial_limit=%" PRIu64 "\n",
|
||||
env->is_in_heapsnapshot_heap_limit_callback_,
|
||||
@@ -2308,6 +2349,85 @@ size_t Environment::NearHeapLimitCallback(void* data,
|
||||
return new_limit;
|
||||
}
|
||||
|
||||
size_t Environment::HeapProfileNearHeapLimitCallback(
|
||||
void* data, size_t current_heap_limit, size_t initial_heap_limit) {
|
||||
auto* env = static_cast<Environment*>(data);
|
||||
|
||||
Debug(env,
|
||||
DebugCategory::DIAGNOSTICS,
|
||||
"Invoked HeapProfileNearHeapLimitCallback, processing=%d, "
|
||||
"current_limit=%" PRIu64 ", "
|
||||
"initial_limit=%" PRIu64 "\n",
|
||||
env->is_in_heap_profile_near_heap_limit_callback_,
|
||||
static_cast<uint64_t>(current_heap_limit),
|
||||
static_cast<uint64_t>(initial_heap_limit));
|
||||
|
||||
const size_t max_young_gen_size = env->isolate_data()->max_young_gen_size;
|
||||
const size_t new_limit = current_heap_limit + max_young_gen_size;
|
||||
|
||||
if (env->is_in_heap_profile_near_heap_limit_callback_) {
|
||||
return new_limit;
|
||||
}
|
||||
|
||||
env->is_in_heap_profile_near_heap_limit_callback_ = true;
|
||||
auto reset_in_callback = OnScopeLeave(
|
||||
[env]() { env->is_in_heap_profile_near_heap_limit_callback_ = false; });
|
||||
auto restore_initial_limit = OnScopeLeave(
|
||||
[env]() { env->isolate()->AutomaticallyRestoreInitialHeapLimit(0.95); });
|
||||
auto uninstall_callback = [env]() {
|
||||
env->RemoveHeapProfileNearHeapLimitCallback(0);
|
||||
};
|
||||
|
||||
std::string dir = env->options()->diagnostic_dir;
|
||||
if (dir.empty()) {
|
||||
dir = Environment::GetCwd(env->exec_path_);
|
||||
}
|
||||
DiagnosticFilename name(env, "Heap", "heapprofile");
|
||||
std::string filename = dir + kPathSeparator + (*name);
|
||||
|
||||
Debug(env, DebugCategory::DIAGNOSTICS, "Writing %s...\n", *name);
|
||||
|
||||
std::ostringstream out;
|
||||
if (!node::SerializeHeapProfile(env->isolate(), out)) {
|
||||
Debug(env,
|
||||
DebugCategory::DIAGNOSTICS,
|
||||
"No sampling heap profile active; uninstalling callback.\n");
|
||||
uninstall_callback();
|
||||
return new_limit;
|
||||
}
|
||||
|
||||
std::string profile = std::move(out).str();
|
||||
uv_buf_t buffer = uv_buf_init(profile.data(), profile.size());
|
||||
const int err = WriteFileSync(filename.c_str(), buffer);
|
||||
if (err != 0) {
|
||||
FPrintF(stderr,
|
||||
"Failed to write heap profile %s: %s\n",
|
||||
filename,
|
||||
uv_strerror(err));
|
||||
uninstall_callback();
|
||||
return new_limit;
|
||||
}
|
||||
|
||||
env->heap_limit_profile_taken_ += 1;
|
||||
FPrintF(stderr, "Wrote heap profile to %s\n", filename);
|
||||
|
||||
Debug(env,
|
||||
DebugCategory::DIAGNOSTICS,
|
||||
"%" PRIu32 "/%" PRIu32 " heap profiles written.\n",
|
||||
env->heap_limit_profile_taken_,
|
||||
env->heap_profile_near_heap_limit_);
|
||||
|
||||
if (env->heap_limit_profile_taken_ == env->heap_profile_near_heap_limit_) {
|
||||
Debug(env,
|
||||
DebugCategory::DIAGNOSTICS,
|
||||
"Removing the near heap limit callback");
|
||||
uninstall_callback();
|
||||
}
|
||||
|
||||
// Returning a larger value is required by V8 even after the final write.
|
||||
return new_limit;
|
||||
}
|
||||
|
||||
inline size_t Environment::SelfSize() const {
|
||||
size_t size = sizeof(*this);
|
||||
// Remove non pointer fields that will be tracked in MemoryInfo()
|
||||
|
||||
@@ -999,6 +999,12 @@ class Environment final : public MemoryRetainer {
|
||||
static size_t NearHeapLimitCallback(void* data,
|
||||
size_t current_heap_limit,
|
||||
size_t initial_heap_limit);
|
||||
static size_t HeapSnapshotNearHeapLimitCallback(void* data,
|
||||
size_t current_heap_limit,
|
||||
size_t initial_heap_limit);
|
||||
static size_t HeapProfileNearHeapLimitCallback(void* data,
|
||||
size_t current_heap_limit,
|
||||
size_t initial_heap_limit);
|
||||
static void BuildEmbedderGraph(v8::Isolate* isolate,
|
||||
v8::EmbedderGraph* graph,
|
||||
void* data);
|
||||
@@ -1076,13 +1082,17 @@ class Environment final : public MemoryRetainer {
|
||||
|
||||
inline void set_heap_snapshot_near_heap_limit(uint32_t limit);
|
||||
inline bool is_in_heapsnapshot_heap_limit_callback() const;
|
||||
inline void set_heap_profile_near_heap_limit(uint32_t limit);
|
||||
inline bool is_in_heap_profile_near_heap_limit_callback() const;
|
||||
|
||||
inline bool report_exclude_env() const;
|
||||
|
||||
inline void AddHeapSnapshotNearHeapLimitCallback();
|
||||
|
||||
inline void RemoveHeapSnapshotNearHeapLimitCallback(size_t heap_limit);
|
||||
|
||||
inline void AddHeapProfileNearHeapLimitCallback();
|
||||
inline void RemoveHeapProfileNearHeapLimitCallback(size_t heap_limit);
|
||||
|
||||
v8::CpuProfilingResult StartCpuProfile(const CpuProfileOptions& options);
|
||||
v8::CpuProfile* StopCpuProfile(v8::ProfilerId profile_id);
|
||||
|
||||
@@ -1182,6 +1192,11 @@ class Environment final : public MemoryRetainer {
|
||||
uint32_t heap_snapshot_near_heap_limit_ = 0;
|
||||
bool heapsnapshot_near_heap_limit_callback_added_ = false;
|
||||
|
||||
bool is_in_heap_profile_near_heap_limit_callback_ = false;
|
||||
uint32_t heap_limit_profile_taken_ = 0;
|
||||
uint32_t heap_profile_near_heap_limit_ = 0;
|
||||
bool heap_profile_near_heap_limit_callback_added_ = false;
|
||||
|
||||
uint32_t module_id_counter_ = 0;
|
||||
uint32_t script_id_counter_ = 0;
|
||||
uint32_t function_id_counter_ = 0;
|
||||
|
||||
@@ -48,7 +48,6 @@ bool SerializeHeapProfile(Isolate* isolate, std::ostringstream& out_stream) {
|
||||
if (!profile) {
|
||||
return false;
|
||||
}
|
||||
profiler->StopSamplingHeapProfiler();
|
||||
JSONWriter writer(out_stream, true);
|
||||
writer.json_start();
|
||||
|
||||
|
||||
@@ -213,6 +213,23 @@ void SetHeapSnapshotNearHeapLimit(const FunctionCallbackInfo<Value>& args) {
|
||||
env->set_heap_snapshot_near_heap_limit(limit);
|
||||
}
|
||||
|
||||
void SetHeapProfileNearHeapLimit(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK(args[0]->IsUint32());
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
uint32_t limit = args[0].As<v8::Uint32>()->Value();
|
||||
CHECK_GT(limit, 0);
|
||||
|
||||
std::string dir = env->options()->diagnostic_dir;
|
||||
if (dir.empty()) {
|
||||
dir = Environment::GetCwd(env->exec_path());
|
||||
}
|
||||
THROW_IF_INSUFFICIENT_PERMISSIONS(
|
||||
env, permission::PermissionScope::kFileSystemWrite, dir);
|
||||
|
||||
env->AddHeapProfileNearHeapLimitCallback();
|
||||
env->set_heap_profile_near_heap_limit(limit);
|
||||
}
|
||||
|
||||
void UpdateHeapStatisticsBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
BindingData* data = Realm::GetBindingData<BindingData>(args);
|
||||
HeapStatistics s;
|
||||
@@ -309,6 +326,7 @@ void StopHeapProfile(const FunctionCallbackInfo<Value>& args) {
|
||||
std::ostringstream out_stream;
|
||||
bool success = node::SerializeHeapProfile(isolate, out_stream);
|
||||
if (success) {
|
||||
isolate->GetHeapProfiler()->StopSamplingHeapProfiler();
|
||||
Local<Value> result;
|
||||
if (ToV8Value(env->context(), out_stream.str(), isolate).ToLocal(&result)) {
|
||||
args.GetReturnValue().Set(result);
|
||||
@@ -728,6 +746,10 @@ void Initialize(Local<Object> target,
|
||||
target,
|
||||
"setHeapSnapshotNearHeapLimit",
|
||||
SetHeapSnapshotNearHeapLimit);
|
||||
SetMethod(context,
|
||||
target,
|
||||
"setHeapProfileNearHeapLimit",
|
||||
SetHeapProfileNearHeapLimit);
|
||||
SetMethod(context,
|
||||
target,
|
||||
"updateHeapStatisticsBuffer",
|
||||
@@ -839,6 +861,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
registry->Register(SetFlagsFromString);
|
||||
registry->Register(GetHashSeed);
|
||||
registry->Register(SetHeapSnapshotNearHeapLimit);
|
||||
registry->Register(SetHeapProfileNearHeapLimit);
|
||||
registry->Register(GCProfiler::New);
|
||||
registry->Register(GCProfiler::Start);
|
||||
registry->Register(GCProfiler::Stop);
|
||||
|
||||
@@ -278,6 +278,7 @@ size_t Worker::NearHeapLimit(void* data, size_t current_heap_limit,
|
||||
Environment* env = worker->env();
|
||||
if (env != nullptr) {
|
||||
DCHECK(!env->is_in_heapsnapshot_heap_limit_callback());
|
||||
DCHECK(!env->is_in_heap_profile_near_heap_limit_callback());
|
||||
Debug(env,
|
||||
DebugCategory::DIAGNOSTICS,
|
||||
"Throwing ERR_WORKER_OUT_OF_MEMORY, "
|
||||
@@ -1107,6 +1108,9 @@ void Worker::StopHeapProfile(const FunctionCallbackInfo<Value>& args) {
|
||||
std::ostringstream out_stream;
|
||||
bool success =
|
||||
node::SerializeHeapProfile(worker_env->isolate(), out_stream);
|
||||
if (success) {
|
||||
worker_env->isolate()->GetHeapProfiler()->StopSamplingHeapProfiler();
|
||||
}
|
||||
env->SetImmediateThreadsafe(
|
||||
[taker = std::move(taker),
|
||||
out_stream = std::move(out_stream),
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const v8 = require('v8');
|
||||
|
||||
v8.setHeapSnapshotNearHeapLimit(1);
|
||||
v8.startHeapProfile();
|
||||
v8.setHeapProfileNearHeapLimit(1);
|
||||
|
||||
require(path.resolve(__dirname, 'grow.js'));
|
||||
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const v8 = require('v8');
|
||||
|
||||
v8.setHeapProfileNearHeapLimit(1);
|
||||
|
||||
require(path.resolve(__dirname, 'grow.js'));
|
||||
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const v8 = require('v8');
|
||||
|
||||
v8.startHeapProfile();
|
||||
v8.setHeapProfileNearHeapLimit(1);
|
||||
|
||||
require(path.resolve(__dirname, 'grow.js'));
|
||||
@@ -43,3 +43,12 @@ const path = require('path');
|
||||
permission: 'FileSystemWrite',
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
assert.throws(() => {
|
||||
v8.setHeapProfileNearHeapLimit(1);
|
||||
}, common.expectsError({
|
||||
code: 'ERR_ACCESS_DENIED',
|
||||
permission: 'FileSystemWrite',
|
||||
}));
|
||||
}
|
||||
@@ -44,6 +44,12 @@ assert.throws(
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
|
||||
const invalidLimits = [-1, 0, '', {}, NaN, undefined];
|
||||
for (const value of invalidLimits) {
|
||||
assert.throws(() => v8.setHeapProfileNearHeapLimit(value),
|
||||
/ERR_INVALID_ARG_TYPE|ERR_OUT_OF_RANGE/);
|
||||
}
|
||||
|
||||
// Default params.
|
||||
{
|
||||
const handle = v8.startHeapProfile();
|
||||
@@ -73,3 +79,11 @@ assert.throws(
|
||||
JSON.parse(handle.stop());
|
||||
assert.strictEqual(handle.stop(), undefined);
|
||||
}
|
||||
|
||||
// Profile and snapshot near-heap-limit callbacks coexist.
|
||||
{
|
||||
v8.setHeapProfileNearHeapLimit(1);
|
||||
v8.setHeapProfileNearHeapLimit(1); // no-op
|
||||
v8.setHeapSnapshotNearHeapLimit(1);
|
||||
v8.setHeapSnapshotNearHeapLimit(1); // no-op
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
if (common.isPi()) {
|
||||
common.skip('Too slow for Raspberry Pi devices');
|
||||
}
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const { spawnSync } = require('child_process');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const fs = require('fs');
|
||||
|
||||
tmpdir.refresh();
|
||||
const child = spawnSync(process.execPath, [
|
||||
'--max-old-space-size=50',
|
||||
fixtures.path('workload', 'heap-profile-and-snapshot-near-heap-limit.js'),
|
||||
], {
|
||||
cwd: tmpdir.path,
|
||||
});
|
||||
|
||||
const stderr = child.stderr.toString();
|
||||
assert(common.nodeProcessAborted(child.status, child.signal),
|
||||
'process should have aborted, but did not');
|
||||
|
||||
const files = fs.readdirSync(tmpdir.path);
|
||||
const snapshots = files.filter((f) => f.endsWith('.heapsnapshot'));
|
||||
const profiles = files.filter((f) => f.endsWith('.heapprofile'));
|
||||
|
||||
assert(snapshots.length === 1 ||
|
||||
stderr.includes('Not generating snapshots because it\'s too risky'));
|
||||
assert.strictEqual(profiles.length, 1);
|
||||
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
if (common.isPi()) {
|
||||
common.skip('Too slow for Raspberry Pi devices');
|
||||
}
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
const assert = require('assert');
|
||||
const { spawnSync } = require('child_process');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const fs = require('fs');
|
||||
|
||||
tmpdir.refresh();
|
||||
const child = spawnSync(process.execPath, [
|
||||
'--max-old-space-size=50',
|
||||
fixtures.path('workload', 'heap-profile-near-heap-limit.js'),
|
||||
], {
|
||||
cwd: tmpdir.path,
|
||||
});
|
||||
|
||||
assert(common.nodeProcessAborted(child.status, child.signal),
|
||||
'process should have aborted, but did not');
|
||||
|
||||
const profiles = fs.readdirSync(tmpdir.path)
|
||||
.filter((file) => file.endsWith('.heapprofile'));
|
||||
assert.strictEqual(profiles.length, 1);
|
||||
|
||||
const profile = JSON.parse(
|
||||
fs.readFileSync(tmpdir.resolve(profiles[0]), 'utf8'));
|
||||
assert(profile.head);
|
||||
assert(profile.samples.length > 0);
|
||||
Reference in new issue
Block a user