Skip to content

Commit ba4847e

Browse files
misterpoeMatt Loring
authored and
Matt Loring
committed
src: Node Tracing Controller
This commit adds support for trace-event tracing to Node.js. It provides a mechanism to centralize tracing information generated by V8, Node core, and userspace code. It includes: - A trace writer responsible for serializing traces and cycling the output files so that no individual file becomes to large. - A buffer for aggregating traces to allow for batched flushes. - An agent which initializes the tracing controller and ensures that trace serialization is done on a separate thread. - A set of macros for generating trace events. - Tests and documentation. Author: Raymond Kang <[email protected]> Author: Kelvin Jin <[email protected]> Author: Matthew Loring <[email protected]> Author: Jason Ginchereau <[email protected]> PR-URL: #9304 Reviewed-By: Trevor Norris <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Josh Gavant <[email protected]>
1 parent 6137983 commit ba4847e

15 files changed

+2503
-40
lines changed

doc/api/cli.md

+14
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,20 @@ added: v2.1.0
129129
Prints a stack trace whenever synchronous I/O is detected after the first turn
130130
of the event loop.
131131

132+
### `--trace-events-enabled`
133+
<!-- YAML
134+
added: REPLACEME
135+
-->
136+
137+
Enables the collection of trace event tracing information.
138+
139+
### `--trace-event-categories`
140+
<!-- YAML
141+
added: REPLACEME
142+
-->
143+
144+
A comma separated list of categories that should be traced when trace event
145+
tracing is enabled using `--trace-events-enabled`.
132146

133147
### `--zero-fill-buffers`
134148
<!-- YAML

doc/api/debugger.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,4 @@ To start debugging, open the following URL in Chrome:
201201
chrome-devtools://devtools/remote/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=localhost:9229/node
202202
```
203203

204-
[TCP-based protocol]: https://github.com/v8/v8/wiki/Debugging-Protocol
204+
[TCP-based protocol]: https://github.com/v8/v8/wiki/Debugging-Protocol

doc/api/tracing.md

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Tracing
2+
3+
Trace Event provides a mechanism to centralize tracing information generated by
4+
V8, Node core, and userspace code.
5+
6+
Tracing can be enabled by passing the `--trace-events-enabled` flag when starting a
7+
Node.js application.
8+
9+
The set of categories for which traces are recorded can be specified using the
10+
`--trace-event-categories` flag followed by a list of comma separated category names.
11+
By default the `node` and `v8` categories are enabled.
12+
13+
```txt
14+
node --trace-events-enabled --trace-event-categories v8,node server.js
15+
```
16+
17+
Running Node.js with tracing enabled will produce log files that can be opened
18+
in the [`chrome://tracing`](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool)
19+
tab of Chrome.

node.gyp

+5
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@
140140
],
141141

142142
'sources': [
143+
'src/tracing/agent.cc',
144+
'src/tracing/node_trace_buffer.cc',
145+
'src/tracing/node_trace_writer.cc',
146+
'src/tracing/trace_event.cc',
143147
'src/debug-agent.cc',
144148
'src/async-wrap.cc',
145149
'src/env.cc',
@@ -217,6 +221,7 @@
217221
'src/stream_base.h',
218222
'src/stream_base-inl.h',
219223
'src/stream_wrap.h',
224+
'src/tracing/trace_event.h'
220225
'src/tree.h',
221226
'src/util.h',
222227
'src/util-inl.h',

src/node.cc

+73-39
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "req-wrap.h"
3939
#include "req-wrap-inl.h"
4040
#include "string_bytes.h"
41+
#include "tracing/agent.h"
4142
#include "util.h"
4243
#include "uv.h"
4344
#if NODE_USE_V8_PLATFORM
@@ -150,6 +151,8 @@ static node_module* modpending;
150151
static node_module* modlist_builtin;
151152
static node_module* modlist_linked;
152153
static node_module* modlist_addon;
154+
static bool trace_enabled = false;
155+
static const char* trace_enabled_categories = nullptr;
153156

154157
#if defined(NODE_HAVE_I18N_SUPPORT)
155158
// Path to ICU data (for i18n / Intl)
@@ -186,6 +189,7 @@ static uv_async_t dispatch_debug_messages_async;
186189

187190
static Mutex node_isolate_mutex;
188191
static v8::Isolate* node_isolate;
192+
static tracing::Agent* tracing_agent;
189193

190194
static node::DebugOptions debug_options;
191195

@@ -194,6 +198,7 @@ static struct {
194198
void Initialize(int thread_pool_size) {
195199
platform_ = v8::platform::CreateDefaultPlatform(thread_pool_size);
196200
V8::InitializePlatform(platform_);
201+
tracing::TraceEventHelper::SetCurrentPlatform(platform_);
197202
}
198203

199204
void PumpMessageLoop(Isolate* isolate) {
@@ -3368,6 +3373,9 @@ void SetupProcessObject(Environment* env,
33683373

33693374
void SignalExit(int signo) {
33703375
uv_tty_reset_mode();
3376+
if (trace_enabled) {
3377+
tracing_agent->Stop();
3378+
}
33713379
#ifdef __FreeBSD__
33723380
// FreeBSD has a nasty bug, see RegisterSignalHandler for details
33733381
struct sigaction sa;
@@ -3463,64 +3471,70 @@ static void PrintHelp() {
34633471
" node debug script.js [arguments] \n"
34643472
"\n"
34653473
"Options:\n"
3466-
" -v, --version print Node.js version\n"
3467-
" -e, --eval script evaluate script\n"
3468-
" -p, --print evaluate script and print result\n"
3469-
" -c, --check syntax check script without executing\n"
3470-
" -i, --interactive always enter the REPL even if stdin\n"
3471-
" does not appear to be a terminal\n"
3472-
" -r, --require module to preload (option can be repeated)\n"
3473-
" --no-deprecation silence deprecation warnings\n"
3474-
" --trace-deprecation show stack traces on deprecations\n"
3475-
" --throw-deprecation throw an exception anytime a deprecated "
3474+
" -v, --version print Node.js version\n"
3475+
" -e, --eval script evaluate script\n"
3476+
" -p, --print evaluate script and print result\n"
3477+
" -c, --check syntax check script without executing\n"
3478+
" -i, --interactive always enter the REPL even if stdin\n"
3479+
" does not appear to be a terminal\n"
3480+
" -r, --require module to preload (option can be "
3481+
"repeated)\n"
3482+
" --no-deprecation silence deprecation warnings\n"
3483+
" --trace-deprecation show stack traces on deprecations\n"
3484+
" --throw-deprecation throw an exception anytime a deprecated "
34763485
"function is used\n"
3477-
" --no-warnings silence all process warnings\n"
3478-
" --trace-warnings show stack traces on process warnings\n"
3479-
" --trace-sync-io show stack trace when use of sync IO\n"
3480-
" is detected after the first tick\n"
3481-
" --track-heap-objects track heap object allocations for heap "
3486+
" --no-warnings silence all process warnings\n"
3487+
" --trace-warnings show stack traces on process warnings\n"
3488+
" --trace-sync-io show stack trace when use of sync IO\n"
3489+
" is detected after the first tick\n"
3490+
" --trace-events-enabled track trace events\n"
3491+
" --trace-event-categories comma separated list of trace event\n"
3492+
" categories to record\n"
3493+
" --track-heap-objects track heap object allocations for heap "
34823494
"snapshots\n"
3483-
" --prof-process process v8 profiler output generated\n"
3484-
" using --prof\n"
3485-
" --zero-fill-buffers automatically zero-fill all newly allocated\n"
3486-
" Buffer and SlowBuffer instances\n"
3487-
" --v8-options print v8 command line options\n"
3488-
" --v8-pool-size=num set v8's thread pool size\n"
3495+
" --prof-process process v8 profiler output generated\n"
3496+
" using --prof\n"
3497+
" --zero-fill-buffers automatically zero-fill all newly "
3498+
"allocated\n"
3499+
" Buffer and SlowBuffer instances\n"
3500+
" --v8-options print v8 command line options\n"
3501+
" --v8-pool-size=num set v8's thread pool size\n"
34893502
#if HAVE_OPENSSL
3490-
" --tls-cipher-list=val use an alternative default TLS cipher list\n"
3503+
" --tls-cipher-list=val use an alternative default TLS cipher "
3504+
"list\n"
34913505
#if NODE_FIPS_MODE
3492-
" --enable-fips enable FIPS crypto at startup\n"
3493-
" --force-fips force FIPS crypto (cannot be disabled)\n"
3506+
" --enable-fips enable FIPS crypto at startup\n"
3507+
" --force-fips force FIPS crypto (cannot be disabled)\n"
34943508
#endif /* NODE_FIPS_MODE */
3495-
" --openssl-config=path load OpenSSL configuration file from the\n"
3496-
" specified path\n"
3509+
" --openssl-config=path load OpenSSL configuration file from the\n"
3510+
" specified path\n"
34973511
#endif /* HAVE_OPENSSL */
34983512
#if defined(NODE_HAVE_I18N_SUPPORT)
3499-
" --icu-data-dir=dir set ICU data load path to dir\n"
3500-
" (overrides NODE_ICU_DATA)\n"
3513+
" --icu-data-dir=dir set ICU data load path to dir\n"
3514+
" (overrides NODE_ICU_DATA)\n"
35013515
#if !defined(NODE_HAVE_SMALL_ICU)
3502-
" note: linked-in ICU data is\n"
3503-
" present.\n"
3516+
" note: linked-in ICU data is\n"
3517+
" present.\n"
35043518
#endif
3505-
" --preserve-symlinks preserve symbolic links when resolving\n"
3506-
" and caching modules.\n"
3519+
" --preserve-symlinks preserve symbolic links when resolving\n"
3520+
" and caching modules.\n"
35073521
#endif
35083522
"\n"
35093523
"Environment variables:\n"
35103524
#ifdef _WIN32
3511-
"NODE_PATH ';'-separated list of directories\n"
3525+
"NODE_PATH ';'-separated list of directories\n"
35123526
#else
3513-
"NODE_PATH ':'-separated list of directories\n"
3527+
"NODE_PATH ':'-separated list of directories\n"
35143528
#endif
3515-
" prefixed to the module search path.\n"
3516-
"NODE_DISABLE_COLORS set to 1 to disable colors in the REPL\n"
3529+
" prefixed to the module search path.\n"
3530+
"NODE_DISABLE_COLORS set to 1 to disable colors in the REPL\n"
35173531
#if defined(NODE_HAVE_I18N_SUPPORT)
3518-
"NODE_ICU_DATA data path for ICU (Intl object) data\n"
3532+
"NODE_ICU_DATA data path for ICU (Intl object) data\n"
35193533
#if !defined(NODE_HAVE_SMALL_ICU)
3520-
" (will extend linked-in data)\n"
3534+
" (will extend linked-in data)\n"
35213535
#endif
35223536
#endif
3523-
"NODE_REPL_HISTORY path to the persistent REPL history file\n"
3537+
"NODE_REPL_HISTORY path to the persistent REPL history file\n"
35243538
"\n"
35253539
"Documentation can be found at https://nodejs.org/\n");
35263540
}
@@ -3626,6 +3640,16 @@ static void ParseArgs(int* argc,
36263640
trace_deprecation = true;
36273641
} else if (strcmp(arg, "--trace-sync-io") == 0) {
36283642
trace_sync_io = true;
3643+
} else if (strcmp(arg, "--trace-events-enabled") == 0) {
3644+
trace_enabled = true;
3645+
} else if (strcmp(arg, "--trace-event-categories") == 0) {
3646+
const char* categories = argv[index + 1];
3647+
if (categories == nullptr) {
3648+
fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
3649+
exit(9);
3650+
}
3651+
args_consumed += 1;
3652+
trace_enabled_categories = categories;
36293653
} else if (strcmp(arg, "--track-heap-objects") == 0) {
36303654
track_heap_objects = true;
36313655
} else if (strcmp(arg, "--throw-deprecation") == 0) {
@@ -4454,10 +4478,20 @@ int Start(int argc, char** argv) {
44544478
#endif
44554479

44564480
v8_platform.Initialize(v8_thread_pool_size);
4481+
// Enable tracing when argv has --trace-events-enabled.
4482+
if (trace_enabled) {
4483+
fprintf(stderr, "Warning: Trace event is an experimental feature "
4484+
"and could change at any time.\n");
4485+
tracing_agent = new tracing::Agent();
4486+
tracing_agent->Start(v8_platform.platform_, trace_enabled_categories);
4487+
}
44574488
V8::Initialize();
44584489
v8_initialized = true;
44594490
const int exit_code =
44604491
Start(uv_default_loop(), argc, argv, exec_argc, exec_argv);
4492+
if (trace_enabled) {
4493+
tracing_agent->Stop();
4494+
}
44614495
v8_initialized = false;
44624496
V8::Dispose();
44634497

src/node.h

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
#include "v8.h" // NOLINT(build/include_order)
4343
#include "node_version.h" // NODE_MODULE_VERSION
44+
#include "tracing/trace_event.h"
4445

4546
#define NODE_MAKE_VERSION(major, minor, patch) \
4647
((major) * 0x1000 + (minor) * 0x100 + (patch))

src/tracing/agent.cc

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include "tracing/agent.h"
2+
3+
#include <sstream>
4+
#include <string>
5+
6+
#include "env-inl.h"
7+
#include "libplatform/libplatform.h"
8+
9+
namespace node {
10+
namespace tracing {
11+
12+
using v8::platform::tracing::TraceConfig;
13+
14+
Agent::Agent() {}
15+
16+
void Agent::Start(v8::Platform* platform, const char* enabled_categories) {
17+
platform_ = platform;
18+
19+
int err = uv_loop_init(&tracing_loop_);
20+
CHECK_EQ(err, 0);
21+
22+
NodeTraceWriter* trace_writer = new NodeTraceWriter(&tracing_loop_);
23+
TraceBuffer* trace_buffer = new NodeTraceBuffer(
24+
NodeTraceBuffer::kBufferChunks, trace_writer, &tracing_loop_);
25+
26+
tracing_controller_ = new TracingController();
27+
28+
TraceConfig* trace_config = new TraceConfig();
29+
if (enabled_categories) {
30+
std::stringstream category_list(enabled_categories);
31+
while (category_list.good()) {
32+
std::string category;
33+
getline(category_list, category, ',');
34+
trace_config->AddIncludedCategory(category.c_str());
35+
}
36+
} else {
37+
trace_config->AddIncludedCategory("v8");
38+
trace_config->AddIncludedCategory("node");
39+
}
40+
41+
// This thread should be created *after* async handles are created
42+
// (within NodeTraceWriter and NodeTraceBuffer constructors).
43+
// Otherwise the thread could shut down prematurely.
44+
err = uv_thread_create(&thread_, ThreadCb, this);
45+
CHECK_EQ(err, 0);
46+
47+
tracing_controller_->Initialize(trace_buffer);
48+
tracing_controller_->StartTracing(trace_config);
49+
v8::platform::SetTracingController(platform, tracing_controller_);
50+
}
51+
52+
void Agent::Stop() {
53+
if (!IsStarted()) {
54+
return;
55+
}
56+
// Perform final Flush on TraceBuffer. We don't want the tracing controller
57+
// to flush the buffer again on destruction of the V8::Platform.
58+
tracing_controller_->StopTracing();
59+
delete tracing_controller_;
60+
// Thread should finish when the tracing loop is stopped.
61+
uv_thread_join(&thread_);
62+
v8::platform::SetTracingController(platform_, nullptr);
63+
}
64+
65+
// static
66+
void Agent::ThreadCb(void* arg) {
67+
Agent* agent = static_cast<Agent*>(arg);
68+
uv_run(&agent->tracing_loop_, UV_RUN_DEFAULT);
69+
}
70+
71+
} // namespace tracing
72+
} // namespace node

src/tracing/agent.h

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef SRC_TRACING_AGENT_H_
2+
#define SRC_TRACING_AGENT_H_
3+
4+
#include "tracing/node_trace_buffer.h"
5+
#include "tracing/node_trace_writer.h"
6+
#include "uv.h"
7+
#include "v8.h"
8+
9+
namespace node {
10+
namespace tracing {
11+
12+
class Agent {
13+
public:
14+
explicit Agent();
15+
void Start(v8::Platform* platform, const char* enabled_categories);
16+
void Stop();
17+
18+
private:
19+
bool IsStarted() { return platform_ != nullptr; }
20+
static void ThreadCb(void* arg);
21+
22+
uv_thread_t thread_;
23+
uv_loop_t tracing_loop_;
24+
v8::Platform* platform_ = nullptr;
25+
TracingController* tracing_controller_;
26+
};
27+
28+
} // namespace tracing
29+
} // namespace node
30+
31+
#endif // SRC_TRACING_AGENT_H_

0 commit comments

Comments
 (0)