diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 30490b90e77b64..d9b2c76ede38c0 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -107,6 +107,18 @@ void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) { env->async_hooks_promise_resolve_function()); } +void AsyncWrap::EmitTraceAsyncStart() const { + if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( + TRACING_CATEGORY_NODE1(async_hooks))) { + tracing::AsyncWrapArgs data(env()->execution_async_id(), + get_trigger_async_id()); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(async_hooks), + provider_names[provider_type()], + static_cast(get_async_id()), + "data", + tracing::CastTracedValue(data)); + } +} void AsyncWrap::EmitTraceEventBefore() { switch (provider_type()) { @@ -601,27 +613,7 @@ void AsyncWrap::AsyncReset(Local resource, double execution_async_id) { } } - switch (provider_type()) { -#define V(PROVIDER) \ - case PROVIDER_ ## PROVIDER: \ - if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \ - TRACING_CATEGORY_NODE1(async_hooks))) { \ - auto data = tracing::TracedValue::Create(); \ - data->SetInteger("executionAsyncId", \ - static_cast(env()->execution_async_id())); \ - data->SetInteger("triggerAsyncId", \ - static_cast(get_trigger_async_id())); \ - TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( \ - TRACING_CATEGORY_NODE1(async_hooks), \ - #PROVIDER, static_cast(get_async_id()), \ - "data", std::move(data)); \ - } \ - break; - NODE_ASYNC_PROVIDER_TYPES(V) -#undef V - default: - UNREACHABLE(); - } + EmitTraceAsyncStart(); context_frame_.Reset(isolate, async_context_frame::current(isolate)); diff --git a/src/async_wrap.h b/src/async_wrap.h index adb0c64bbb8eb8..37e77fe3294ba6 100644 --- a/src/async_wrap.h +++ b/src/async_wrap.h @@ -178,6 +178,7 @@ class AsyncWrap : public BaseObject { void EmitDestroy(bool from_gc = false); + void EmitTraceAsyncStart() const; void EmitTraceEventBefore(); static void EmitTraceEventAfter(ProviderType type, double async_id); void EmitTraceEventDestroy(); diff --git a/src/env.cc b/src/env.cc index b242a53f94e10b..052d3ba0b5a782 100644 --- a/src/env.cc +++ b/src/env.cc @@ -894,18 +894,12 @@ Environment::Environment(IsolateData* isolate_data, if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( TRACING_CATEGORY_NODE1(environment)) != 0) { - auto traced_value = tracing::TracedValue::Create(); - traced_value->BeginArray("args"); - for (const std::string& arg : args) traced_value->AppendString(arg); - traced_value->EndArray(); - traced_value->BeginArray("exec_args"); - for (const std::string& arg : exec_args) traced_value->AppendString(arg); - traced_value->EndArray(); + tracing::EnvironmentArgs traced_value(args, exec_args); TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(environment), "Environment", this, "args", - std::move(traced_value)); + tracing::CastTracedValue(traced_value)); } if (options_->permission) { diff --git a/src/node_metadata.cc b/src/node_metadata.cc index 87aa96cfdc5858..6544999b84dc0f 100644 --- a/src/node_metadata.cc +++ b/src/node_metadata.cc @@ -161,6 +161,28 @@ Metadata::Versions::Versions() { nbytes = NBYTES_VERSION; } +std::array, + NODE_VERSIONS_KEY_COUNT> +Metadata::Versions::pairs() const { + std::array, + NODE_VERSIONS_KEY_COUNT> + versions_array; + auto slot = versions_array.begin(); + +#define V(key) \ + do { \ + *slot++ = std::pair( \ + #key, per_process::metadata.versions.key); \ + } while (0); + NODE_VERSIONS_KEYS(V) +#undef V + + std::ranges::sort(versions_array, + [](auto& a, auto& b) { return a.first < b.first; }); + + return versions_array; +} + Metadata::Release::Release() : name(NODE_RELEASE) { #if NODE_VERSION_IS_LTS lts = NODE_VERSION_LTS_CODENAME; diff --git a/src/node_metadata.h b/src/node_metadata.h index 7b2072ad39c3f1..d9c533f100d25a 100644 --- a/src/node_metadata.h +++ b/src/node_metadata.h @@ -3,7 +3,9 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include #include +#include #include "node_version.h" #if HAVE_OPENSSL @@ -98,6 +100,10 @@ namespace node { NODE_VERSIONS_KEY_QUIC(V) \ NODE_VERSIONS_KEY_SQLITE(V) +#define V(key) +1 +constexpr int NODE_VERSIONS_KEY_COUNT = NODE_VERSIONS_KEYS(V); +#undef V + class Metadata { public: Metadata(); @@ -118,6 +124,10 @@ class Metadata { #define V(key) std::string key; NODE_VERSIONS_KEYS(V) #undef V + + std::array, + NODE_VERSIONS_KEY_COUNT> + pairs() const; }; struct Release { diff --git a/src/node_process_object.cc b/src/node_process_object.cc index 5138ef7d616787..5f5af63486a556 100644 --- a/src/node_process_object.cc +++ b/src/node_process_object.cc @@ -83,24 +83,7 @@ static void SetVersions(Isolate* isolate, Local versions) { READONLY_STRING_PROPERTY( versions, "node", per_process::metadata.versions.node); -#define V(key) +1 - std::pair - versions_array[NODE_VERSIONS_KEYS(V)]; -#undef V - auto* slot = &versions_array[0]; - -#define V(key) \ - do { \ - *slot++ = std::pair( \ - #key, per_process::metadata.versions.key); \ - } while (0); - NODE_VERSIONS_KEYS(V) -#undef V - - std::ranges::sort(versions_array, - [](auto& a, auto& b) { return a.first < b.first; }); - - for (const auto& version : versions_array) { + for (const auto& version : per_process::metadata.versions.pairs()) { versions ->DefineOwnProperty(context, OneByteString(isolate, version.first), diff --git a/src/node_report.cc b/src/node_report.cc index 2dcdfa8b139cf7..f4ae5c4457dc00 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -797,24 +797,7 @@ static void PrintComponentVersions(JSONWriter* writer) { writer->json_objectstart("componentVersions"); -#define V(key) +1 - std::pair - versions_array[NODE_VERSIONS_KEYS(V)]; -#undef V - auto* slot = &versions_array[0]; - -#define V(key) \ - do { \ - *slot++ = std::pair( \ - #key, per_process::metadata.versions.key); \ - } while (0); - NODE_VERSIONS_KEYS(V) -#undef V - - std::ranges::sort(versions_array, - [](auto& a, auto& b) { return a.first < b.first; }); - - for (const auto& version : versions_array) { + for (const auto& version : per_process::metadata.versions.pairs()) { writer->json_keyvalue(version.first, version.second); } diff --git a/src/node_v8_platform-inl.h b/src/node_v8_platform-inl.h index fc0922035aac07..57a725a988f445 100644 --- a/src/node_v8_platform-inl.h +++ b/src/node_v8_platform-inl.h @@ -38,31 +38,11 @@ class NodeTraceStateObserver TRACE_EVENT_METADATA1( "__metadata", "thread_name", "name", "JavaScriptMainThread"); - auto trace_process = tracing::TracedValue::Create(); - trace_process->BeginDictionary("versions"); - -#define V(key) \ - trace_process->SetString(#key, per_process::metadata.versions.key.c_str()); - - NODE_VERSIONS_KEYS(V) -#undef V - - trace_process->EndDictionary(); - - trace_process->SetString("arch", per_process::metadata.arch.c_str()); - trace_process->SetString("platform", - per_process::metadata.platform.c_str()); - - trace_process->BeginDictionary("release"); - trace_process->SetString("name", - per_process::metadata.release.name.c_str()); -#if NODE_VERSION_IS_LTS - trace_process->SetString("lts", per_process::metadata.release.lts.c_str()); -#endif - trace_process->EndDictionary(); - TRACE_EVENT_METADATA1( - "__metadata", "node", "process", std::move(trace_process)); - + tracing::ProcessMeta trace_process; + TRACE_EVENT_METADATA1("__metadata", + "node", + "process", + tracing::CastTracedValue(trace_process)); // This only runs the first time tracing is enabled controller_->RemoveTraceStateObserver(this); } diff --git a/src/tracing/traced_value.cc b/src/tracing/traced_value.cc index 662b0357a19305..46c15ae8f1669c 100644 --- a/src/tracing/traced_value.cc +++ b/src/tracing/traced_value.cc @@ -9,10 +9,10 @@ #include #endif -#include -#include #include -#include + +#include "node_metadata.h" +#include "util.h" #if defined(_STLP_VENDOR_CSTD) // STLPort doesn't import fpclassify into the std namespace. @@ -218,5 +218,44 @@ void TracedValue::AppendAsTraceFormat(std::string* out) const { *out += root_is_array_ ? ']' : '}'; } +std::unique_ptr EnvironmentArgs::Cast() const { + auto traced_value = tracing::TracedValue::Create(); + traced_value->BeginArray("args"); + for (const std::string& arg : args_) traced_value->AppendString(arg); + traced_value->EndArray(); + traced_value->BeginArray("exec_args"); + for (const std::string& arg : exec_args_) traced_value->AppendString(arg); + traced_value->EndArray(); + return traced_value; +} + +std::unique_ptr AsyncWrapArgs::Cast() const { + auto data = tracing::TracedValue::Create(); + data->SetInteger("executionAsyncId", execution_async_id_); + data->SetInteger("triggerAsyncId", trigger_async_id_); + return data; +} + +std::unique_ptr ProcessMeta::Cast() const { + auto trace_process = tracing::TracedValue::Create(); + trace_process->BeginDictionary("versions"); + for (const auto& version : per_process::metadata.versions.pairs()) { + trace_process->SetString(version.first.data(), version.second.data()); + } + trace_process->EndDictionary(); + + trace_process->SetString("arch", per_process::metadata.arch.c_str()); + trace_process->SetString("platform", per_process::metadata.platform.c_str()); + + trace_process->BeginDictionary("release"); + trace_process->SetString("name", per_process::metadata.release.name.c_str()); +#if NODE_VERSION_IS_LTS + trace_process->SetString("lts", per_process::metadata.release.lts.c_str()); +#endif + trace_process->EndDictionary(); + + return trace_process; +} + } // namespace tracing } // namespace node diff --git a/src/tracing/traced_value.h b/src/tracing/traced_value.h index 93c5be799b63a4..0bc9df81d87562 100644 --- a/src/tracing/traced_value.h +++ b/src/tracing/traced_value.h @@ -5,17 +5,76 @@ #ifndef SRC_TRACING_TRACED_VALUE_H_ #define SRC_TRACING_TRACED_VALUE_H_ -#include "node.h" -#include "util.h" -#include "v8.h" +#include "v8-platform.h" -#include -#include +#include +#include #include namespace node { namespace tracing { +template +std::unique_ptr CastTracedValue(const T& value) { + return value.Cast(); +} + +class EnvironmentArgs { + public: + EnvironmentArgs(std::span args, + std::span exec_args) + : args_(args), exec_args_(exec_args) {} + + std::unique_ptr Cast() const; + + private: + std::span args_; + std::span exec_args_; +}; + +class AsyncWrapArgs { + public: + AsyncWrapArgs(int64_t execution_async_id, int64_t trigger_async_id) + : execution_async_id_(execution_async_id), + trigger_async_id_(trigger_async_id) {} + + std::unique_ptr Cast() const; + + private: + int64_t execution_async_id_; + int64_t trigger_async_id_; +}; + +class ProcessMeta { + public: + std::unique_ptr Cast() const; +}; + +// Do not use this class directly. Define a custom structured class to provide +// a conversion method so that the class can be used with both V8 legacy +// trace API and perfetto API. +// +// These classes provide a JSON-inspired way to write structed data into traces. +// +// To define how a custom class should be written into the trace, users should +// define one of the two following functions: +// - Foo::Cast(TracedValue) const +// (preferred for code which depends on perfetto directly) +// +// After defining a conversion method, the object can be used as a +// TRACE_EVENT argument: +// +// Foo foo; +// TRACE_EVENT("cat", "Event", "arg", CastTracedValue(foo)); +// +// class Foo { +// std::unique_ptr Cast() const { +// auto traced_value = tracing::TracedValue::Create(); +// dict->SetInteger("key", 42); +// dict->SetString("foo", "bar"); +// return traced_value; +// } +// } class TracedValue : public v8::ConvertableToTraceFormat { public: ~TracedValue() override = default; diff --git a/test/cctest/test_traced_value.cc b/test/cctest/test_traced_value.cc index fa938c881ef06c..f877e9d0b00e87 100644 --- a/test/cctest/test_traced_value.cc +++ b/test/cctest/test_traced_value.cc @@ -94,3 +94,29 @@ TEST(TracedValue, EscapingArray) { EXPECT_EQ(check, string); } + +TEST(TracedValue, EnvironmentArgs) { + std::vector args{"a", "bb", "ccc"}; + std::vector exec_args{"--inspect", "--a-long-arg"}; + node::tracing::EnvironmentArgs env_args(args, exec_args); + + std::string string; + env_args.Cast()->AppendAsTraceFormat(&string); + + static const char* check = "{\"args\":[\"a\",\"bb\",\"ccc\"]," + "\"exec_args\":[\"--inspect\",\"--a-long-arg\"]}"; + + EXPECT_EQ(check, string); +} + +TEST(TracedValue, AsyncWrapArgs) { + node::tracing::AsyncWrapArgs aw_args(1, 1); + + std::string string; + aw_args.Cast()->AppendAsTraceFormat(&string); + + static const char* check = "{\"executionAsyncId\":1," + "\"triggerAsyncId\":1}"; + + EXPECT_EQ(check, string); +}