Skip to content

Commit

Permalink
v8: make writeHeapSnapshot throw if fopen fails
Browse files Browse the repository at this point in the history
If the file fails to be written (e.g. missing permissions, no space left
on device, etc), `writeHeapSnapshot` will now throw an exception.

This commit also adds error handling for the `fclose` call, returning
false if a non-zero value was returned.

Fixes: #41346
  • Loading branch information
kyranet committed Jan 1, 2022
1 parent a4795ad commit a694441
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1643,7 +1643,7 @@ size_t Environment::NearHeapLimitCallback(void* data,
env->isolate()->RemoveNearHeapLimitCallback(NearHeapLimitCallback,
initial_heap_limit);

heap::WriteSnapshot(env->isolate(), filename.c_str());
heap::WriteSnapshot(env, filename.c_str());
env->heap_limit_snapshot_taken_ += 1;

// Don't take more snapshots than the number specified by
Expand Down
21 changes: 13 additions & 8 deletions src/heap_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -308,21 +308,26 @@ class HeapSnapshotStream : public AsyncWrap,
HeapSnapshotPointer snapshot_;
};

inline void TakeSnapshot(Isolate* isolate, v8::OutputStream* out) {
inline void TakeSnapshot(Environment* env, v8::OutputStream* out) {
HeapSnapshotPointer snapshot {
isolate->GetHeapProfiler()->TakeHeapSnapshot() };
env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
snapshot->Serialize(out, HeapSnapshot::kJSON);
}

} // namespace

bool WriteSnapshot(Isolate* isolate, const char* filename) {
bool WriteSnapshot(Environment* env, const char* filename) {
FILE* fp = fopen(filename, "w");
if (fp == nullptr)
if (fp == nullptr) {
env->ThrowErrnoException(errno, "fopen");
return false;
}
FileOutputStream stream(fp);
TakeSnapshot(isolate, &stream);
fclose(fp);
TakeSnapshot(env, &stream);
if (fclose(fp)) {
env->ThrowErrnoException(errno, "fclose");
return false;
}
return true;
}

Expand Down Expand Up @@ -374,7 +379,7 @@ void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {

if (filename_v->IsUndefined()) {
DiagnosticFilename name(env, "Heap", "heapsnapshot");
if (!WriteSnapshot(isolate, *name))
if (!WriteSnapshot(env, *name))
return;
if (String::NewFromUtf8(isolate, *name).ToLocal(&filename_v)) {
args.GetReturnValue().Set(filename_v);
Expand All @@ -384,7 +389,7 @@ void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {

BufferValue path(isolate, filename_v);
CHECK_NOT_NULL(*path);
if (!WriteSnapshot(isolate, *path))
if (!WriteSnapshot(env, *path))
return;
return args.GetReturnValue().Set(filename_v);
}
Expand Down
2 changes: 1 addition & 1 deletion src/node_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ class DiagnosticFilename {
};

namespace heap {
bool WriteSnapshot(v8::Isolate* isolate, const char* filename);
bool WriteSnapshot(Environment* env, const char* filename);
}

class TraceEventScope {
Expand Down
12 changes: 12 additions & 0 deletions test/sequential/test-heapdump.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ process.chdir(tmpdir.path);
fs.accessSync(heapdump);
}

{
const readonlyFile = 'ro';
fs.writeFileSync(readonlyFile, Buffer.alloc(0), { mode: 0o444 });
assert.throws(() => {
writeHeapSnapshot(readonlyFile);
}, (e) => {
assert.ok(e, 'writeHeapSnapshot should error');
assert.strictEqual(e.code, 'EACCES');
return true;
});
}

[1, true, {}, [], null, Infinity, NaN].forEach((i) => {
assert.throws(() => writeHeapSnapshot(i), {
code: 'ERR_INVALID_ARG_TYPE',
Expand Down

0 comments on commit a694441

Please sign in to comment.