Skip to content

Commit a8b5f8f

Browse files
committed
src: trace threadpool event
1 parent 9878c26 commit a8b5f8f

8 files changed

+119
-21
lines changed

doc/api/tracing.md

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ The available categories are:
2222
* `node.bootstrap`: Enables capture of Node.js bootstrap milestones.
2323
* `node.console`: Enables capture of `console.time()` and `console.count()`
2424
output.
25+
* `node.threadpoolwork`: Enables capture of trace data for threadpool
26+
asynchronous operations, such as `blob`, `zlib`, `crypto` and `node_api`.
2527
* `node.dns.native`: Enables capture of trace data for DNS queries.
2628
* `node.net.native`: Enables capture of trace data for network.
2729
* `node.environment`: Enables capture of Node.js Environment milestones.

src/crypto/crypto_util.h

+6-7
Original file line numberDiff line numberDiff line change
@@ -333,14 +333,13 @@ class CryptoJob : public AsyncWrap, public ThreadPoolWork {
333333
public:
334334
using AdditionalParams = typename CryptoJobTraits::AdditionalParameters;
335335

336-
explicit CryptoJob(
337-
Environment* env,
338-
v8::Local<v8::Object> object,
339-
AsyncWrap::ProviderType type,
340-
CryptoJobMode mode,
341-
AdditionalParams&& params)
336+
explicit CryptoJob(Environment* env,
337+
v8::Local<v8::Object> object,
338+
AsyncWrap::ProviderType type,
339+
CryptoJobMode mode,
340+
AdditionalParams&& params)
342341
: AsyncWrap(env, object, type),
343-
ThreadPoolWork(env),
342+
ThreadPoolWork(env, "crypto"),
344343
mode_(mode),
345344
params_(std::move(params)) {
346345
// If the CryptoJob is async, then the instance will be

src/node_api.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork {
10691069
env->isolate,
10701070
async_resource,
10711071
*v8::String::Utf8Value(env->isolate, async_resource_name)),
1072-
ThreadPoolWork(env->node_env()),
1072+
ThreadPoolWork(env->node_env(), "node_api"),
10731073
_env(env),
10741074
_data(data),
10751075
_execute(execute),

src/node_blob.cc

+5-6
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,12 @@ void Blob::GetDataObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
306306
}
307307
}
308308

309-
FixedSizeBlobCopyJob::FixedSizeBlobCopyJob(
310-
Environment* env,
311-
Local<Object> object,
312-
Blob* blob,
313-
FixedSizeBlobCopyJob::Mode mode)
309+
FixedSizeBlobCopyJob::FixedSizeBlobCopyJob(Environment* env,
310+
Local<Object> object,
311+
Blob* blob,
312+
FixedSizeBlobCopyJob::Mode mode)
314313
: AsyncWrap(env, object, AsyncWrap::PROVIDER_FIXEDSIZEBLOBCOPY),
315-
ThreadPoolWork(env),
314+
ThreadPoolWork(env, "blob"),
316315
mode_(mode) {
317316
if (mode == FixedSizeBlobCopyJob::Mode::SYNC) MakeWeak();
318317
source_ = blob->entries();

src/node_internals.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ class DebugSealHandleScope {
258258

259259
class ThreadPoolWork {
260260
public:
261-
explicit inline ThreadPoolWork(Environment* env) : env_(env) {
261+
explicit inline ThreadPoolWork(Environment* env, const char* type)
262+
: env_(env), type_(type) {
262263
CHECK_NOT_NULL(env);
263264
}
264265
inline virtual ~ThreadPoolWork() = default;
@@ -274,6 +275,8 @@ class ThreadPoolWork {
274275
private:
275276
Environment* env_;
276277
uv_work_t work_req_;
278+
const char* type_;
279+
uint64_t time_ = 0;
277280
};
278281

279282
#define TRACING_CATEGORY_NODE "node"

src/node_zlib.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork {
259259

260260
CompressionStream(Environment* env, Local<Object> wrap)
261261
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_ZLIB),
262-
ThreadPoolWork(env),
262+
ThreadPoolWork(env, "zlib"),
263263
write_result_(nullptr) {
264264
MakeWeak();
265265
}

src/threadpoolwork-inl.h

+28-5
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,46 @@
2424

2525
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
2626

27-
#include "util-inl.h"
2827
#include "node_internals.h"
28+
#include "tracing/trace_event.h"
29+
#include "util-inl.h"
2930

3031
namespace node {
3132

3233
void ThreadPoolWork::ScheduleWork() {
3334
env_->IncreaseWaitingRequestCounter();
35+
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
36+
TRACING_CATEGORY_NODE1(threadpoolwork), type_, this);
37+
bool enabled = false;
38+
uv_work_cb fn = [](uv_work_t* req) {
39+
ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req);
40+
self->DoThreadPoolWork();
41+
};
42+
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
43+
TRACING_CATEGORY_NODE1(threadpoolwork))) {
44+
enabled = true;
45+
fn = [](uv_work_t* req) {
46+
ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req);
47+
uint64_t start = uv_hrtime();
48+
self->DoThreadPoolWork();
49+
self->time_ = uv_hrtime() - start;
50+
};
51+
}
3452
int status = uv_queue_work(
3553
env_->event_loop(),
3654
&work_req_,
37-
[](uv_work_t* req) {
38-
ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req);
39-
self->DoThreadPoolWork();
40-
},
55+
fn,
4156
[](uv_work_t* req, int status) {
4257
ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req);
4358
self->env_->DecreaseWaitingRequestCounter();
59+
TRACE_EVENT_NESTABLE_ASYNC_END2(
60+
TRACING_CATEGORY_NODE1(threadpoolwork),
61+
self->type_,
62+
self,
63+
"result",
64+
status,
65+
"syncTime",
66+
self->time_);
4467
self->AfterThreadPoolWork(status);
4568
});
4669
CHECK_EQ(status, 0);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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 path = require('path');
7+
const tmpdir = require('../common/tmpdir');
8+
9+
if (!common.hasCrypto)
10+
common.skip('missing crypto');
11+
12+
const { hkdf } = require('crypto');
13+
const { deflate } = require('zlib');
14+
const { Blob } = require('buffer');
15+
16+
if (process.env.isChild === '1') {
17+
hkdf('sha512', 'key', 'salt', 'info', 64, () => {});
18+
deflate('hello', () => {});
19+
// Make async call
20+
const blob = new Blob(['h'.repeat(4096 * 2)]);
21+
blob.arrayBuffer();
22+
return;
23+
}
24+
25+
tmpdir.refresh();
26+
const FILE_NAME = path.join(tmpdir.path, 'node_trace.1.log');
27+
28+
cp.spawnSync(process.execPath,
29+
[
30+
'--trace-events-enabled',
31+
'--trace-event-categories',
32+
'node.threadpoolwork',
33+
__filename,
34+
],
35+
{
36+
cwd: tmpdir.path,
37+
env: {
38+
...process.env,
39+
isChild: '1',
40+
},
41+
});
42+
43+
assert(fs.existsSync(FILE_NAME));
44+
const data = fs.readFileSync(FILE_NAME);
45+
const traces = JSON.parse(data.toString()).traceEvents;
46+
47+
assert(traces.length > 0);
48+
49+
let blobCount = 0;
50+
let zlibCount = 0;
51+
let cryptoCount = 0;
52+
53+
traces.forEach((item) => {
54+
if (item.cat === 'node,node.threadpoolwork') {
55+
if (item.ph === 'e') {
56+
assert.ok(item.args.result === 0);
57+
assert.ok(item.args.syncTime >= 0);
58+
}
59+
if (item.name === 'blob') {
60+
blobCount++;
61+
} else if (item.name === 'zlib') {
62+
zlibCount++;
63+
} else if (item.name === 'crypto') {
64+
cryptoCount++;
65+
}
66+
}
67+
});
68+
69+
// There are three types, each type has two async events at least
70+
assert.ok(blobCount >= 2);
71+
assert.ok(zlibCount >= 2);
72+
assert.ok(cryptoCount >= 2);

0 commit comments

Comments
 (0)