Skip to content

src,lib: use uv_thread_setname to a better multi-thread debugging #56416

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
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
15 changes: 12 additions & 3 deletions doc/api/worker_threads.md
Original file line number Diff line number Diff line change
Expand Up @@ -1229,9 +1229,18 @@ changes:
used for generated code.
* `stackSizeMb` {number} The default maximum stack size for the thread.
Small values may lead to unusable Worker instances. **Default:** `4`.
* `name` {string} An optional `name` to be appended to the worker title
for debugging/identification purposes, making the final title as
`[worker ${id}] ${name}`. **Default:** `''`.
* `name` {string} An optional `name` to be replaced in the thread name
and to the worker title for debugging/identification purposes,
making the final title as `[worker ${id}] ${name}`.
This parameter has a maximum allowed size, depending on the operating
system. If the provided name exceeds the limit, it will be truncated
* Maximum sizes:
* Windows: 32,767 characters
* macOS: 64 characters
* Linux: 16 characters
* NetBSD: limited to `PTHREAD_MAX_NAMELEN_NP`
* FreeBSD and OpenBSD: limited to `MAXCOMLEN`
**Default:** `'WorkerThread'`.

### Event: `'error'`

Expand Down
2 changes: 1 addition & 1 deletion lib/internal/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ class Worker extends EventEmitter {
options.env);
}

let name = '';
let name = 'WorkerThread';
if (options.name) {
validateString(options.name, 'options.name');
name = StringPrototypeTrim(options.name);
Expand Down
1 change: 1 addition & 0 deletions src/inspector_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ static void StartIoThreadWakeup(int signo, siginfo_t* info, void* ucontext) {
}

inline void* StartIoThreadMain(void* unused) {
uv_thread_setname("SignalInspector");
for (;;) {
uv_sem_wait(&start_io_thread_semaphore);
Mutex::ScopedLock lock(start_io_thread_async_mutex);
Expand Down
5 changes: 5 additions & 0 deletions src/inspector_io.cc
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,11 @@ void InspectorIo::ThreadMain(void* io) {
}

void InspectorIo::ThreadMain() {
int thread_name_error = uv_thread_setname("InspectorIo");
if (!thread_name_error) [[unlikely]] {
per_process::Debug(node::DebugCategory::INSPECTOR_SERVER,
"Failed to set thread name for Inspector\n");
}
uv_loop_t loop;
loop.data = nullptr;
int err = uv_loop_init(&loop);
Expand Down
1 change: 1 addition & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,7 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
}

if (!(flags & ProcessInitializationFlags::kNoInitializeNodeV8Platform)) {
uv_thread_setname("MainThread");
Copy link
Member Author

Choose a reason for hiding this comment

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

I wonder if that's the best place to do it.

per_process::v8_platform.Initialize(
static_cast<int>(per_process::cli_options->v8_thread_pool_size));
result->platform_ = per_process::v8_platform.Platform();
Expand Down
1 change: 1 addition & 0 deletions src/node_platform.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct PlatformWorkerData {
};

static void PlatformWorkerThread(void* data) {
uv_thread_setname("V8Worker");
std::unique_ptr<PlatformWorkerData>
worker_data(static_cast<PlatformWorkerData*>(data));

Expand Down
1 change: 1 addition & 0 deletions src/node_worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@ void Worker::StartThread(const FunctionCallbackInfo<Value>& args) {
Worker* w = static_cast<Worker*>(arg);
const uintptr_t stack_top = reinterpret_cast<uintptr_t>(&arg);

uv_thread_setname(w->name_.c_str());
// Leave a few kilobytes just to make sure we're within limits and have
// some space to do work in C++ land.
w->stack_base_ = stack_top - (w->stack_size_ - kStackBufferSize);
Expand Down
29 changes: 29 additions & 0 deletions test/addons/uv-thread-name/binding.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <node.h>
#include <uv.h>
#include <v8.h>

using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::String;
using v8::Value;

void GetThreadName(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
uv_thread_t thread;
char thread_name[16];
#ifdef _WIN32
/* uv_thread_self isn't defined for the main thread on Windows. */
thread = GetCurrentThread();
#else
thread = uv_thread_self();
#endif
uv_thread_getname(&thread, thread_name, sizeof(thread_name));
args.GetReturnValue().Set(
String::NewFromUtf8(isolate, thread_name).ToLocalChecked());
}

void init(v8::Local<v8::Object> exports) {
NODE_SET_METHOD(exports, "getThreadName", GetThreadName);
}

NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, init)
9 changes: 9 additions & 0 deletions test/addons/uv-thread-name/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
'targets': [
{
'target_name': 'binding',
'sources': [ 'binding.cc' ],
'includes': ['../common.gypi'],
}
]
}
35 changes: 35 additions & 0 deletions test/addons/uv-thread-name/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';
const common = require('../../common');

if (common.isAIX) {
common.skip('AIX is not supported by libuv');
}

const assert = require('node:assert');
const { parentPort, Worker, isMainThread } = require('node:worker_threads');
const bindingPath = require.resolve(`./build/${common.buildType}/binding`);
const binding = require(bindingPath);

if (isMainThread) {
assert.strictEqual(binding.getThreadName(), 'MainThread');

const worker = new Worker(__filename);
worker.on('message', common.mustCall((data) => {
assert.strictEqual(data, 'WorkerThread');
}));
worker.on('error', common.mustNotCall());
worker.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 0);
}));

const namedWorker = new Worker(__filename, { name: 'NamedThread' });
namedWorker.on('message', common.mustCall((data) => {
assert.strictEqual(data, 'NamedThread');
}));
namedWorker.on('error', common.mustNotCall());
namedWorker.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 0);
}));
} else {
parentPort.postMessage(binding.getThreadName());
}
2 changes: 1 addition & 1 deletion test/parallel/test-trace-events-worker-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ if (isMainThread) {
assert(traces.length > 0);
assert(traces.some((trace) =>
trace.cat === '__metadata' && trace.name === 'thread_name' &&
trace.args.name === '[worker 1]'));
trace.args.name === '[worker 1] WorkerThread'));
}));
}));
} else {
Expand Down
Loading