Skip to content

Commit

Permalink
worker,v8: add function to get isolate address
Browse files Browse the repository at this point in the history
This change adds an experimental function `v8.getIsolateAddress()`,
which returns the address of the currently executing isolate. It is
intended to be used by application developers to correlate the output of
`--trace_gc` with application-level log lines.

Fixes: nodejs#43810
  • Loading branch information
kvakil committed Jul 19, 2022
1 parent dabda03 commit 1781a01
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 2 deletions.
22 changes: 22 additions & 0 deletions doc/api/v8.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,27 @@ buffers and external strings.
}
```

## `v8.getIsolateAddress()`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental
* Returns: {string}

The address of the V8 Isolate corresponding to the current [worker
thread][worker threads]. You can add this to your application-level logs
lines to correlate it with the `--trace_gc` log lines output by V8. The
exact format of this identifier is platform-dependent.

This identifier is stable while the current worker thread is executing.
However the address may be reused by other threads after this worker
thread stops. See [`require('node:worker_threads').threadId`][] for an
identifier which is guaranteed not to be reused over the process's
lifetime.

## `v8.setFlagsFromString(flags)`

<!-- YAML
Expand Down Expand Up @@ -1028,6 +1049,7 @@ Returns true if the Node.js instance is run to build a snapshot.
[`deserializer._readHostObject()`]: #deserializer_readhostobject
[`deserializer.transferArrayBuffer()`]: #deserializertransferarraybufferid-arraybuffer
[`init` callback]: #initpromise-parent
[`require('node:worker_threads').threadId`]: #workerthreadid
[`serialize()`]: #v8serializevalue
[`serializer._getSharedArrayBufferId()`]: #serializer_getsharedarraybufferidsharedarraybuffer
[`serializer._writeHostObject()`]: #serializer_writehostobjectobject
Expand Down
2 changes: 2 additions & 0 deletions lib/v8.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const binding = internalBinding('v8');

const {
cachedDataVersionTag,
getIsolateAddress,
setFlagsFromString: _setFlagsFromString,
updateHeapStatisticsBuffer,
updateHeapSpaceStatisticsBuffer,
Expand Down Expand Up @@ -376,6 +377,7 @@ module.exports = {
getHeapStatistics,
getHeapSpaceStatistics,
getHeapCodeStatistics,
getIsolateAddress,
setFlagsFromString,
Serializer,
Deserializer,
Expand Down
14 changes: 13 additions & 1 deletion src/node_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,15 @@ void CachedDataVersionTag(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(result);
}

void GetIsolateAddress(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
char address[64];
// Use %p (even though it's platform dependent) because that's what V8 uses
// for --trace-gc, and we want to match it.
snprintf(address, sizeof(address), "%p", isolate);
args.GetReturnValue().Set(OneByteString(isolate, address));
}

void UpdateHeapStatisticsBuffer(const FunctionCallbackInfo<Value>& args) {
BindingData* data = Environment::GetBindingData<BindingData>(args);
HeapStatistics s;
Expand Down Expand Up @@ -190,7 +199,6 @@ void UpdateHeapCodeStatisticsBuffer(const FunctionCallbackInfo<Value>& args) {
#undef V
}


void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsString());
String::Utf8Value flags(args.GetIsolate(), args[0]);
Expand All @@ -208,6 +216,9 @@ void Initialize(Local<Object> target,

env->SetMethodNoSideEffect(target, "cachedDataVersionTag",
CachedDataVersionTag);

env->SetMethodNoSideEffect(target, "getIsolateAddress", GetIsolateAddress);

env->SetMethod(
target, "updateHeapStatisticsBuffer", UpdateHeapStatisticsBuffer);

Expand Down Expand Up @@ -253,6 +264,7 @@ void Initialize(Local<Object> target,

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(CachedDataVersionTag);
registry->Register(GetIsolateAddress);
registry->Register(UpdateHeapStatisticsBuffer);
registry->Register(UpdateHeapCodeStatisticsBuffer);
registry->Register(UpdateHeapSpaceStatisticsBuffer);
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/gc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
const { getIsolateAddress } = require('v8');
console.log('my isolate address:', getIsolateAddress());

let arr = new Array(300_000).fill('a');

for (let index = 0; index < arr.length; index++) {
Expand Down
17 changes: 16 additions & 1 deletion test/v8-updates/test-trace-gc-flag.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ const fixtures = require('../common/fixtures');
const output = childProcess.stdout.toString().trim();
const lines = splitByLine(output);

const isolateAddressRegex = /^my isolate address: (.*)$/;
const isolateAddressLine = lines.find((line) => line.match(isolateAddressRegex));
assert(isolateAddressLine);
const isolateAddress = isolateAddressLine.match(isolateAddressRegex)[1];
assert(isolateAddress);

const traceGcLines = lines.filter((line) => line !== isolateAddressLine);

const scavengeRegex = /\bScavenge\b/;
const expectedOutput = [
scavengeRegex,
Expand All @@ -26,9 +34,16 @@ const fixtures = require('../common/fixtures');
scavengeRegex,
/\bMark-sweep\b/,
];
lines.forEach((line, index) => {

assert.strictEqual(traceGcLines.length, expectedOutput.length);
traceGcLines.forEach((line, index) => {
assert.match(line, expectedOutput[index]);
});

const traceGcPrefix = `[${childProcess.pid}:${isolateAddress}] `;
traceGcLines.forEach((line) => {
assert(line.startsWith(traceGcPrefix));
});
}

/**
Expand Down

0 comments on commit 1781a01

Please sign in to comment.