diff --git a/src/node.cc b/src/node.cc index 627f4958e0278c..a31bcacc1f7ffa 100644 --- a/src/node.cc +++ b/src/node.cc @@ -30,6 +30,7 @@ #include "node_debug_options.h" #include "node_perf.h" #include "node_context_data.h" +#include "tracing/traced_value.h" #if defined HAVE_PERFCTR #include "node_counters.h" @@ -289,11 +290,106 @@ static v8::Isolate* node_isolate; DebugOptions debug_options; +// Ensures that __metadata trace events are only emitted +// when tracing is enabled. +class NodeTraceStateObserver : + public v8::TracingController::TraceStateObserver { + public: + void OnTraceEnabled() override { + char name_buffer[512]; + if (uv_get_process_title(name_buffer, sizeof(name_buffer)) == 0) { + // Only emit the metadata event if the title can be retrieved + // successfully. Ignore it otherwise. + TRACE_EVENT_METADATA1("__metadata", "process_name", + "name", TRACE_STR_COPY(name_buffer)); + } + TRACE_EVENT_METADATA1("__metadata", "version", + "node", NODE_VERSION_STRING); + TRACE_EVENT_METADATA1("__metadata", "thread_name", + "name", "JavaScriptMainThread"); + + auto trace_process = tracing::TracedValue::Create(); + trace_process->BeginDictionary("versions"); + + const char http_parser_version[] = + NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR) + "." + NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR) + "." + NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH); + + const char node_napi_version[] = NODE_STRINGIFY(NAPI_VERSION); + const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION); + + trace_process->SetString("http_parser", http_parser_version); + trace_process->SetString("node", NODE_VERSION_STRING); + trace_process->SetString("v8", V8::GetVersion()); + trace_process->SetString("uv", uv_version_string()); + trace_process->SetString("zlib", ZLIB_VERSION); + trace_process->SetString("ares", ARES_VERSION_STR); + trace_process->SetString("modules", node_modules_version); + trace_process->SetString("nghttp2", NGHTTP2_VERSION); + trace_process->SetString("napi", node_napi_version); + +#if HAVE_OPENSSL + // Stupid code to slice out the version string. + { // NOLINT(whitespace/braces) + size_t i, j, k; + int c; + for (i = j = 0, k = sizeof(OPENSSL_VERSION_TEXT) - 1; i < k; ++i) { + c = OPENSSL_VERSION_TEXT[i]; + if ('0' <= c && c <= '9') { + for (j = i + 1; j < k; ++j) { + c = OPENSSL_VERSION_TEXT[j]; + if (c == ' ') + break; + } + break; + } + } + trace_process->SetString("openssl", + std::string(&OPENSSL_VERSION_TEXT[i], j - i)); + } +#endif + trace_process->EndDictionary(); + + trace_process->SetString("arch", NODE_ARCH); + trace_process->SetString("platform", NODE_PLATFORM); + + trace_process->BeginDictionary("release"); + trace_process->SetString("name", NODE_RELEASE); +#if NODE_VERSION_IS_LTS + trace_process->SetString("lts", NODE_VERSION_LTS_CODENAME); +#endif + trace_process->EndDictionary(); + TRACE_EVENT_METADATA1("__metadata", "node", + "process", std::move(trace_process)); + + // This only runs the first time tracing is enabled + controller_->RemoveTraceStateObserver(this); + delete this; + } + + void OnTraceDisabled() override { + // Do nothing here. This should never be called because the + // observer removes itself when OnTraceEnabled() is called. + UNREACHABLE(); + } + + explicit NodeTraceStateObserver(v8::TracingController* controller) : + controller_(controller) {} + ~NodeTraceStateObserver() override {} + + private: + v8::TracingController* controller_; +}; + static struct { #if NODE_USE_V8_PLATFORM void Initialize(int thread_pool_size) { tracing_agent_.reset(new tracing::Agent(trace_file_pattern)); auto controller = tracing_agent_->GetTracingController(); + controller->AddTraceStateObserver(new NodeTraceStateObserver(controller)); tracing::TraceEventHelper::SetTracingController(controller); StartTracingAgent(); platform_ = new NodePlatform(thread_pool_size, controller); @@ -1992,7 +2088,6 @@ void SetupProcessObject(Environment* env, READONLY_PROPERTY(versions, "http_parser", FIXED_ONE_BYTE_STRING(env->isolate(), http_parser_version)); - // +1 to get rid of the leading 'v' READONLY_PROPERTY(versions, "node", @@ -2015,11 +2110,9 @@ void SetupProcessObject(Environment* env, versions, "modules", FIXED_ONE_BYTE_STRING(env->isolate(), node_modules_version)); - READONLY_PROPERTY(versions, "nghttp2", FIXED_ONE_BYTE_STRING(env->isolate(), NGHTTP2_VERSION)); - const char node_napi_version[] = NODE_STRINGIFY(NAPI_VERSION); READONLY_PROPERTY( versions, @@ -3550,17 +3643,6 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, Environment env(isolate_data, context, v8_platform.GetTracingAgent()); env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling); - char name_buffer[512]; - if (uv_get_process_title(name_buffer, sizeof(name_buffer)) == 0) { - // Only emit the metadata event if the title can be retrieved successfully. - // Ignore it otherwise. - TRACE_EVENT_METADATA1("__metadata", "process_name", "name", - TRACE_STR_COPY(name_buffer)); - } - TRACE_EVENT_METADATA1("__metadata", "version", "node", NODE_VERSION_STRING); - TRACE_EVENT_METADATA1("__metadata", "thread_name", "name", - "JavaScriptMainThread"); - const char* path = argc > 1 ? argv[1] : nullptr; StartInspector(&env, path, debug_options); diff --git a/test/parallel/test-trace-events-metadata.js b/test/parallel/test-trace-events-metadata.js index 7f9ccc3c7378d5..01e019c1906956 100644 --- a/test/parallel/test-trace-events-metadata.js +++ b/test/parallel/test-trace-events-metadata.js @@ -23,25 +23,53 @@ const proc = cp.spawn(process.execPath, proc.once('exit', common.mustCall(() => { assert(common.fileExists(FILE_NAME)); fs.readFile(FILE_NAME, common.mustCall((err, data) => { - const traces = JSON.parse(data.toString()).traceEvents; + const traces = JSON.parse(data.toString()).traceEvents + .filter((trace) => trace.cat === '__metadata'); assert(traces.length > 0); assert(traces.some((trace) => - trace.cat === '__metadata' && trace.name === 'thread_name' && + trace.name === 'thread_name' && trace.args.name === 'JavaScriptMainThread')); assert(traces.some((trace) => - trace.cat === '__metadata' && trace.name === 'thread_name' && + trace.name === 'thread_name' && trace.args.name === 'BackgroundTaskRunner')); assert(traces.some((trace) => - trace.cat === '__metadata' && trace.name === 'version' && + trace.name === 'version' && trace.args.node === process.versions.node)); + + assert(traces.some((trace) => + trace.name === 'node' && + trace.args.process.versions.http_parser === + process.versions.http_parser && + trace.args.process.versions.node === + process.versions.node && + trace.args.process.versions.v8 === + process.versions.v8 && + trace.args.process.versions.uv === + process.versions.uv && + trace.args.process.versions.zlib === + process.versions.zlib && + trace.args.process.versions.ares === + process.versions.ares && + trace.args.process.versions.modules === + process.versions.modules && + trace.args.process.versions.nghttp2 === + process.versions.nghttp2 && + trace.args.process.versions.napi === + process.versions.napi && + trace.args.process.versions.openssl === + process.versions.openssl && + trace.args.process.arch === process.arch && + trace.args.process.platform === process.platform && + trace.args.process.release.name === process.release.name && + (!process.release.lts || + trace.args.process.release.lts === process.release.lts))); + if (!common.isSunOS) { // Changing process.title is currently unsupported on SunOS/SmartOS assert(traces.some((trace) => - trace.cat === '__metadata' && trace.name === 'process_name' && - trace.args.name === 'foo')); + trace.name === 'process_name' && trace.args.name === 'foo')); assert(traces.some((trace) => - trace.cat === '__metadata' && trace.name === 'process_name' && - trace.args.name === 'bar')); + trace.name === 'process_name' && trace.args.name === 'bar')); } })); }));