Skip to content

Commit 2d9bf91

Browse files
authored
worker: add support for worker name in inspector and trace_events
Fixes: #41589 PR-URL: #46832 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Darshan Sen <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent 00981ea commit 2d9bf91

14 files changed

+144
-30
lines changed

doc/api/worker_threads.md

+7
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,10 @@ if (isMainThread) {
906906
<!-- YAML
907907
added: v10.5.0
908908
changes:
909+
- version: REPLACEME
910+
pr-url: https://github.com/nodejs/node/pull/46832
911+
description: Added support for a `name` option, which allows
912+
adding a name to worker title for debugging.
909913
- version: v14.9.0
910914
pr-url: https://github.com/nodejs/node/pull/34584
911915
description: The `filename` parameter can be a WHATWG `URL` object using
@@ -1004,6 +1008,9 @@ changes:
10041008
used for generated code.
10051009
* `stackSizeMb` {number} The default maximum stack size for the thread.
10061010
Small values may lead to unusable Worker instances. **Default:** `4`.
1011+
* `name` {string} An optional `name` to be appended to the worker title
1012+
for debuggin/identification purposes, making the final title as
1013+
`[worker ${id}] ${name}`. **Default:** `''`.
10071014

10081015
### Event: `'error'`
10091016

lib/internal/worker.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const {
1616
SafeArrayIterator,
1717
SafeMap,
1818
String,
19+
StringPrototypeTrim,
1920
Symbol,
2021
SymbolFor,
2122
TypedArrayPrototypeFill,
@@ -57,7 +58,7 @@ const {
5758
const { deserializeError } = require('internal/error_serdes');
5859
const { fileURLToPath, isURL, pathToFileURL } = require('internal/url');
5960
const { kEmptyObject } = require('internal/util');
60-
const { validateArray } = require('internal/validators');
61+
const { validateArray, validateString } = require('internal/validators');
6162

6263
const {
6364
ownsProcessState,
@@ -188,12 +189,19 @@ class Worker extends EventEmitter {
188189
options.env);
189190
}
190191

192+
let name = '';
193+
if (options.name) {
194+
validateString(options.name, 'options.name');
195+
name = StringPrototypeTrim(options.name);
196+
}
197+
191198
// Set up the C++ handle for the worker, as well as some internal wiring.
192199
this[kHandle] = new WorkerImpl(url,
193200
env === process.env ? null : env,
194201
options.execArgv,
195202
parseResourceLimits(options.resourceLimits),
196-
!!(options.trackUnmanagedFds ?? true));
203+
!!(options.trackUnmanagedFds ?? true),
204+
name);
197205
if (this[kHandle].invalidExecArgv) {
198206
throw new ERR_WORKER_INVALID_EXEC_ARGV(this[kHandle].invalidExecArgv);
199207
}

src/api/environment.cc

+7-1
Original file line numberDiff line numberDiff line change
@@ -509,11 +509,17 @@ NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
509509
Environment* env,
510510
ThreadId thread_id,
511511
const char* url) {
512+
return GetInspectorParentHandle(env, thread_id, url, "");
513+
}
514+
515+
NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
516+
Environment* env, ThreadId thread_id, const char* url, const char* name) {
512517
CHECK_NOT_NULL(env);
518+
if (name == nullptr) name = "";
513519
CHECK_NE(thread_id.id, static_cast<uint64_t>(-1));
514520
#if HAVE_INSPECTOR
515521
return std::make_unique<InspectorParentHandleImpl>(
516-
env->inspector_agent()->GetParentHandle(thread_id.id, url));
522+
env->inspector_agent()->GetParentHandle(thread_id.id, url, name));
517523
#else
518524
return {};
519525
#endif

src/inspector/worker_inspector.cc

+14-9
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,20 @@ class WorkerStartedRequest : public Request {
1414
uint64_t id,
1515
const std::string& url,
1616
std::shared_ptr<node::inspector::MainThreadHandle> worker_thread,
17-
bool waiting)
17+
bool waiting,
18+
const std::string& name)
1819
: id_(id),
19-
info_(BuildWorkerTitle(id), url, worker_thread),
20+
info_(BuildWorkerTitle(id, name), url, worker_thread),
2021
waiting_(waiting) {}
2122
void Call(MainThreadInterface* thread) override {
2223
auto manager = thread->inspector_agent()->GetWorkerManager();
2324
manager->WorkerStarted(id_, info_, waiting_);
2425
}
2526

2627
private:
27-
static std::string BuildWorkerTitle(int id) {
28-
return "Worker " + std::to_string(id);
28+
static std::string BuildWorkerTitle(int id, const std::string& name) {
29+
return "[worker " + std::to_string(id) + "]" +
30+
(name == "" ? "" : " " + name);
2931
}
3032

3133
uint64_t id_;
@@ -57,11 +59,13 @@ ParentInspectorHandle::ParentInspectorHandle(
5759
uint64_t id,
5860
const std::string& url,
5961
std::shared_ptr<MainThreadHandle> parent_thread,
60-
bool wait_for_connect)
62+
bool wait_for_connect,
63+
const std::string& name)
6164
: id_(id),
6265
url_(url),
6366
parent_thread_(parent_thread),
64-
wait_(wait_for_connect) {}
67+
wait_(wait_for_connect),
68+
name_(name) {}
6569

6670
ParentInspectorHandle::~ParentInspectorHandle() {
6771
parent_thread_->Post(
@@ -71,7 +75,7 @@ ParentInspectorHandle::~ParentInspectorHandle() {
7175
void ParentInspectorHandle::WorkerStarted(
7276
std::shared_ptr<MainThreadHandle> worker_thread, bool waiting) {
7377
std::unique_ptr<Request> request(
74-
new WorkerStartedRequest(id_, url_, worker_thread, waiting));
78+
new WorkerStartedRequest(id_, url_, worker_thread, waiting, name_));
7579
parent_thread_->Post(std::move(request));
7680
}
7781

@@ -97,9 +101,10 @@ void WorkerManager::WorkerStarted(uint64_t session_id,
97101
}
98102

99103
std::unique_ptr<ParentInspectorHandle> WorkerManager::NewParentHandle(
100-
uint64_t thread_id, const std::string& url) {
104+
uint64_t thread_id, const std::string& url, const std::string& name) {
101105
bool wait = !delegates_waiting_on_start_.empty();
102-
return std::make_unique<ParentInspectorHandle>(thread_id, url, thread_, wait);
106+
return std::make_unique<ParentInspectorHandle>(
107+
thread_id, url, thread_, wait, name);
103108
}
104109

105110
void WorkerManager::RemoveAttachDelegate(int id) {

src/inspector/worker_inspector.h

+7-7
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,13 @@ class ParentInspectorHandle {
5656
ParentInspectorHandle(uint64_t id,
5757
const std::string& url,
5858
std::shared_ptr<MainThreadHandle> parent_thread,
59-
bool wait_for_connect);
59+
bool wait_for_connect,
60+
const std::string& name);
6061
~ParentInspectorHandle();
6162
std::unique_ptr<ParentInspectorHandle> NewParentInspectorHandle(
62-
uint64_t thread_id, const std::string& url) {
63-
return std::make_unique<ParentInspectorHandle>(thread_id,
64-
url,
65-
parent_thread_,
66-
wait_);
63+
uint64_t thread_id, const std::string& url, const std::string& name) {
64+
return std::make_unique<ParentInspectorHandle>(
65+
thread_id, url, parent_thread_, wait_, name);
6766
}
6867
void WorkerStarted(std::shared_ptr<MainThreadHandle> worker_thread,
6968
bool waiting);
@@ -80,6 +79,7 @@ class ParentInspectorHandle {
8079
std::string url_;
8180
std::shared_ptr<MainThreadHandle> parent_thread_;
8281
bool wait_;
82+
std::string name_;
8383
};
8484

8585
class WorkerManager : public std::enable_shared_from_this<WorkerManager> {
@@ -88,7 +88,7 @@ class WorkerManager : public std::enable_shared_from_this<WorkerManager> {
8888
: thread_(thread) {}
8989

9090
std::unique_ptr<ParentInspectorHandle> NewParentHandle(
91-
uint64_t thread_id, const std::string& url);
91+
uint64_t thread_id, const std::string& url, const std::string& name);
9292
void WorkerStarted(uint64_t session_id, const WorkerInfo& info, bool waiting);
9393
void WorkerFinished(uint64_t session_id);
9494
std::unique_ptr<WorkerManagerEventHandle> SetAutoAttach(

src/inspector_agent.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -952,17 +952,17 @@ void Agent::SetParentHandle(
952952
}
953953

954954
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
955-
uint64_t thread_id, const std::string& url) {
955+
uint64_t thread_id, const std::string& url, const std::string& name) {
956956
if (!parent_env_->should_create_inspector() && !client_) {
957957
ThrowUninitializedInspectorError(parent_env_);
958958
return std::unique_ptr<ParentInspectorHandle>{};
959959
}
960960

961961
CHECK_NOT_NULL(client_);
962962
if (!parent_handle_) {
963-
return client_->getWorkerManager()->NewParentHandle(thread_id, url);
963+
return client_->getWorkerManager()->NewParentHandle(thread_id, url, name);
964964
} else {
965-
return parent_handle_->NewParentInspectorHandle(thread_id, url);
965+
return parent_handle_->NewParentInspectorHandle(thread_id, url, name);
966966
}
967967
}
968968

src/inspector_agent.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class Agent {
8282

8383
void SetParentHandle(std::unique_ptr<ParentInspectorHandle> parent_handle);
8484
std::unique_ptr<ParentInspectorHandle> GetParentHandle(
85-
uint64_t thread_id, const std::string& url);
85+
uint64_t thread_id, const std::string& url, const std::string& name);
8686

8787
// Called to create inspector sessions that can be used from the same thread.
8888
// The inspector responds by using the delegate to send messages back.

src/node.h

+6
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,12 @@ NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
679679
ThreadId child_thread_id,
680680
const char* child_url);
681681

682+
NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
683+
Environment* parent_env,
684+
ThreadId child_thread_id,
685+
const char* child_url,
686+
const char* name);
687+
682688
struct StartExecutionCallbackInfo {
683689
v8::Local<v8::Object> process_object;
684690
v8::Local<v8::Function> native_require;

src/node_worker.cc

+15-6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ constexpr double kMB = 1024 * 1024;
5050
Worker::Worker(Environment* env,
5151
Local<Object> wrap,
5252
const std::string& url,
53+
const std::string& name,
5354
std::shared_ptr<PerIsolateOptions> per_isolate_opts,
5455
std::vector<std::string>&& exec_argv,
5556
std::shared_ptr<KVStore> env_vars,
@@ -59,6 +60,7 @@ Worker::Worker(Environment* env,
5960
exec_argv_(exec_argv),
6061
platform_(env->isolate_data()->platform()),
6162
thread_id_(AllocateEnvironmentThreadId()),
63+
name_(name),
6264
env_vars_(env_vars),
6365
snapshot_data_(snapshot_data) {
6466
Debug(this, "Creating new worker instance with thread id %llu",
@@ -83,8 +85,8 @@ Worker::Worker(Environment* env,
8385
Number::New(env->isolate(), static_cast<double>(thread_id_.id)))
8486
.Check();
8587

86-
inspector_parent_handle_ = GetInspectorParentHandle(
87-
env, thread_id_, url.c_str());
88+
inspector_parent_handle_ =
89+
GetInspectorParentHandle(env, thread_id_, url.c_str(), name.c_str());
8890

8991
argv_ = std::vector<std::string>{env->argv()[0]};
9092
// Mark this Worker object as weak until we actually start the thread.
@@ -265,11 +267,10 @@ size_t Worker::NearHeapLimit(void* data, size_t current_heap_limit,
265267
}
266268

267269
void Worker::Run() {
268-
std::string name = "WorkerThread ";
269-
name += std::to_string(thread_id_.id);
270+
std::string trace_name = "[worker " + std::to_string(thread_id_.id) + "]" +
271+
(name_ == "" ? "" : " " + name_);
270272
TRACE_EVENT_METADATA1(
271-
"__metadata", "thread_name", "name",
272-
TRACE_STR_COPY(name.c_str()));
273+
"__metadata", "thread_name", "name", TRACE_STR_COPY(trace_name.c_str()));
273274
CHECK_NOT_NULL(platform_);
274275

275276
Debug(this, "Creating isolate for worker with id %llu", thread_id_.id);
@@ -470,6 +471,7 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
470471
}
471472

472473
std::string url;
474+
std::string name;
473475
std::shared_ptr<PerIsolateOptions> per_isolate_opts = nullptr;
474476
std::shared_ptr<KVStore> env_vars = nullptr;
475477

@@ -482,6 +484,12 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
482484
url.append(value.out(), value.length());
483485
}
484486

487+
if (!args[5]->IsNullOrUndefined()) {
488+
Utf8Value value(
489+
isolate, args[5]->ToString(env->context()).FromMaybe(Local<String>()));
490+
name.append(value.out(), value.length());
491+
}
492+
485493
if (args[1]->IsNull()) {
486494
// Means worker.env = { ...process.env }.
487495
env_vars = env->env_vars()->Clone(isolate);
@@ -592,6 +600,7 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
592600
Worker* worker = new Worker(env,
593601
args.This(),
594602
url,
603+
name,
595604
per_isolate_opts,
596605
std::move(exec_argv_out),
597606
env_vars,

src/node_worker.h

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Worker : public AsyncWrap {
3030
Worker(Environment* env,
3131
v8::Local<v8::Object> wrap,
3232
const std::string& url,
33+
const std::string& name,
3334
std::shared_ptr<PerIsolateOptions> per_isolate_opts,
3435
std::vector<std::string>&& exec_argv,
3536
std::shared_ptr<KVStore> env_vars,
@@ -99,6 +100,8 @@ class Worker : public AsyncWrap {
99100
ExitCode exit_code_ = ExitCode::kNoFailure;
100101
ThreadId thread_id_;
101102
uintptr_t stack_base_ = 0;
103+
// Optional name used for debugging in inspector and trace events.
104+
std::string name_;
102105

103106
// Custom resource constraints:
104107
double resource_limits_[kTotalResourceLimitCount];

test/fixtures/worker-name.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const { Session } = require('inspector');
2+
const { parentPort } = require('worker_threads');
3+
4+
const session = new Session();
5+
6+
parentPort.once('message', () => {}); // Prevent the worker from exiting.
7+
8+
session.connectToMainThread();
9+
10+
session.on(
11+
'NodeWorker.attachedToWorker',
12+
({ params: { workerInfo } }) => {
13+
// send the worker title to the main thread
14+
parentPort.postMessage(workerInfo.title);
15+
}
16+
);
17+
session.post('NodeWorker.enable', { waitForDebuggerOnStart: false });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const cp = require('child_process');
5+
const fs = require('fs');
6+
const { isMainThread } = require('worker_threads');
7+
8+
if (isMainThread) {
9+
const CODE = 'const { Worker } = require(\'worker_threads\'); ' +
10+
`new Worker('${__filename.replace(/\\/g, '/')}', { name: 'foo' })`;
11+
const FILE_NAME = 'node_trace.1.log';
12+
const tmpdir = require('../common/tmpdir');
13+
tmpdir.refresh();
14+
process.chdir(tmpdir.path);
15+
16+
const proc = cp.spawn(process.execPath,
17+
[ '--trace-event-categories', 'node',
18+
'-e', CODE ]);
19+
proc.once('exit', common.mustCall(() => {
20+
assert(fs.existsSync(FILE_NAME));
21+
fs.readFile(FILE_NAME, common.mustCall((err, data) => {
22+
const traces = JSON.parse(data.toString()).traceEvents;
23+
assert(traces.length > 0);
24+
assert(traces.some((trace) =>
25+
trace.cat === '__metadata' && trace.name === 'thread_name' &&
26+
trace.args.name === '[worker 1] foo'));
27+
}));
28+
}));
29+
} else {
30+
// Do nothing here.
31+
}

test/parallel/test-trace-events-worker-metadata.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ if (isMainThread) {
2323
assert(traces.length > 0);
2424
assert(traces.some((trace) =>
2525
trace.cat === '__metadata' && trace.name === 'thread_name' &&
26-
trace.args.name === 'WorkerThread 1'));
26+
trace.args.name === '[worker 1]'));
2727
}));
2828
}));
2929
} else {

test/parallel/test-worker-name.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const fixtures = require('../common/fixtures');
5+
6+
common.skipIfInspectorDisabled();
7+
common.skipIfWorker(); // This test requires both main and worker threads.
8+
9+
const assert = require('assert');
10+
const { Worker, isMainThread } = require('worker_threads');
11+
12+
if (isMainThread) {
13+
const name = 'Hello Thread';
14+
const expectedTitle = `[worker 1] ${name}`;
15+
const worker = new Worker(fixtures.path('worker-name.js'), {
16+
name,
17+
});
18+
worker.once('message', common.mustCall((message) => {
19+
assert.strictEqual(message, expectedTitle);
20+
worker.postMessage('done');
21+
}));
22+
}

0 commit comments

Comments
 (0)