Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions lib/internal/main/mksnapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const { emitExperimentalWarning } = require('internal/util');
const { emitWarningSync } = require('internal/process/warning');

const {
initializeCallbacks,
namespace: {
addDeserializeCallback,
isBuildingSnapshot,
Expand Down Expand Up @@ -139,7 +138,6 @@ function requireForUserSnapshot(id) {

function main() {
prepareMainThreadExecution(false, false);
initializeCallbacks();

// In a context created for building snapshots, V8 does not install Error.stackTraceLimit and as
// a result, if an error is created during the snapshot building process, error.stack would be
Expand Down
2 changes: 1 addition & 1 deletion lib/internal/v8/startup_snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ function setDeserializeMainFunction(callback, data) {
});
}

initializeCallbacks();
module.exports = {
initializeCallbacks,
runDeserializeCallbacks,
throwIfBuildingSnapshot,
// Exposed to require('v8').startupSnapshot
Expand Down
9 changes: 7 additions & 2 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "node.h"
#include "node_builtins.h"
#include "node_context_data.h"
#include "node_debug.h"
#include "node_errors.h"
#include "node_exit_code.h"
#include "node_internals.h"
Expand Down Expand Up @@ -112,17 +113,21 @@ MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,

void* NodeArrayBufferAllocator::Allocate(size_t size) {
void* ret;
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) {
COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.ZeroFilled");
ret = allocator_->Allocate(size);
else
} else {
COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.Uninitialized");
ret = allocator_->AllocateUninitialized(size);
}
if (ret != nullptr) [[likely]] {
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
}
return ret;
}

void* NodeArrayBufferAllocator::AllocateUninitialized(size_t size) {
COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.Uninitialized");
void* ret = allocator_->AllocateUninitialized(size);
if (ret != nullptr) [[likely]] {
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
Expand Down
24 changes: 24 additions & 0 deletions src/node_debug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "v8-fast-api-calls.h"
#include "v8.h"

#include <string>
#include <string_view>
#include <unordered_map>
#endif // DEBUG
Expand All @@ -23,9 +24,20 @@ using v8::Number;
using v8::Object;
using v8::Value;

thread_local std::unordered_map<std::string, int> generic_usage_counters;
thread_local std::unordered_map<FastStringKey, int, FastStringKey::Hash>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this use FastStringKey as well, that is useful here for the same reason as it is for fast API call count tracking

v8_fast_api_call_counts;

void CountGenericUsage(const char* counter_name) {
if (generic_usage_counters.find(counter_name) == generic_usage_counters.end())
generic_usage_counters[counter_name] = 0;
Comment on lines +32 to +33
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (generic_usage_counters.find(counter_name) == generic_usage_counters.end())
generic_usage_counters[counter_name] = 0;

... since this doesn't actually do anything, right? operator[] creates the element for you if it doesn't exist

Copy link
Member

@addaleax addaleax Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do a separate PR – #60447

generic_usage_counters[counter_name]++;
}

int GetGenericUsageCount(const char* counter_name) {
return generic_usage_counters[counter_name];
}

void TrackV8FastApiCall(FastStringKey key) {
v8_fast_api_call_counts[key]++;
}
Expand All @@ -34,6 +46,17 @@ int GetV8FastApiCallCount(FastStringKey key) {
return v8_fast_api_call_counts[key];
}

void GetGenericUsageCount(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args[0]->IsString()) {
env->ThrowError("getGenericUsageCount must be called with a string");
return;
}
Utf8Value utf8_key(env->isolate(), args[0]);
args.GetReturnValue().Set(
GetGenericUsageCount(utf8_key.ToStringView().data()));
}

void GetV8FastApiCallCount(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args[0]->IsString()) {
Expand Down Expand Up @@ -89,6 +112,7 @@ void Initialize(Local<Object> target,
Local<Context> context,
void* priv) {
SetMethod(context, target, "getV8FastApiCallCount", GetV8FastApiCallCount);
SetMethod(context, target, "getGenericUsageCount", GetGenericUsageCount);
SetFastMethod(context, target, "isEven", SlowIsEven, &fast_is_even);
SetFastMethod(context, target, "isOdd", SlowIsOdd, &fast_is_odd);
}
Expand Down
4 changes: 4 additions & 0 deletions src/node_debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ namespace debug {
void TrackV8FastApiCall(FastStringKey key);
int GetV8FastApiCallCount(FastStringKey key);

void CountGenericUsage(const char* counter_name);
#define COUNT_GENERIC_USAGE(name) node::debug::CountGenericUsage(name)

#define TRACK_V8_FAST_API_CALL(key) \
node::debug::TrackV8FastApiCall(FastStringKey(key))
#else // !DEBUG
#define TRACK_V8_FAST_API_CALL(key)
#define COUNT_GENERIC_USAGE(name)
#endif // DEBUG

} // namespace debug
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Flags: --expose-internals --zero-fill-buffers
// Verifies that Buffer.allocUnsafe() allocates initialized memory under --zero-fill-buffers by
// checking the usage count of the relevant native allocator code path.
'use strict';

const common = require('../common');
if (!common.isDebug) {
common.skip('Only works in debug mode');
}
const { internalBinding } = require('internal/test/binding');
const { getGenericUsageCount } = internalBinding('debug');
const assert = require('assert');

const initialUninitializedCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.Uninitialized');
const initialZeroFilledCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.ZeroFilled');
const buffer = Buffer.allocUnsafe(Buffer.poolSize + 1);
assert(buffer.every((b) => b === 0));
const newUninitializedCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.Uninitialized');
const newZeroFilledCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.ZeroFilled');
assert.strictEqual(newUninitializedCount, initialUninitializedCount);
assert.notStrictEqual(newZeroFilledCount, initialZeroFilledCount);
23 changes: 23 additions & 0 deletions test/parallel/test-buffer-alloc-unsafe-is-uninitialized.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Flags: --expose-internals
// Verifies that Buffer.allocUnsafe() indeed allocates uninitialized memory by checking
// the usage count of the relevant native allocator code path.
'use strict';

const common = require('../common');
if (!common.isDebug) {
common.skip('Only works in debug mode');
}
const { internalBinding } = require('internal/test/binding');
const { getGenericUsageCount } = internalBinding('debug');
const assert = require('assert');

const initialUninitializedCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.Uninitialized');
const initialZeroFilledCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.ZeroFilled');
Buffer.allocUnsafe(Buffer.poolSize + 1);
// We can't reliably check the contents of the buffer here because the OS or memory allocator
// used might zero-fill memory for us, or they might happen to return reused memory that was
// previously used by a process that zero-filled it. So instead we just check the usage counts.
const newUninitializedCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.Uninitialized');
const newZeroFilledCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.ZeroFilled');
assert.notStrictEqual(newUninitializedCount, initialUninitializedCount);
assert.strictEqual(newZeroFilledCount, initialZeroFilledCount);
Loading