mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
src: internalize v8::ConvertableToTraceFormat in traces
`v8::ConvertableToTraceFormat` is only available in legacy V8 tracing
API and no longer supported in perfetto. This internalize
`node::tracing::TracedValue` and `v8::ConvertableToTraceFormat` by
defining specialized trace argument classes.
The newly defined structured trace argument classes can be easily
converted to `perfetto::TracedValue` by perfetto traced value protocol.
For example, when adding perfetto support, `CastTracedValue` will be a
no-op and these classes can add a new conversion method like:
```cpp
class Foo {
void WriteIntoTrace(TracedValue context) const {
auto dict = std::move(context).WriteDictionary();
dict->Add("key", 42);
dict->Add("foo", "bar");
dict->Add("member", member_);
}
};
```
PR-URL: https://github.com/nodejs/node/pull/57866
Refs: https://github.com/nodejs/diagnostics/issues/654
Refs: 9ddf987d48/include/perfetto/tracing/traced_value.h (L46)
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
parent
f4413175b4
commit
324b262544
|
|
@ -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<int64_t>(get_async_id()),
|
||||
"data",
|
||||
tracing::CastTracedValue(data));
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWrap::EmitTraceEventBefore() {
|
||||
switch (provider_type()) {
|
||||
|
|
@ -601,27 +613,7 @@ void AsyncWrap::AsyncReset(Local<Object> 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<int64_t>(env()->execution_async_id())); \
|
||||
data->SetInteger("triggerAsyncId", \
|
||||
static_cast<int64_t>(get_trigger_async_id())); \
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( \
|
||||
TRACING_CATEGORY_NODE1(async_hooks), \
|
||||
#PROVIDER, static_cast<int64_t>(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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
10
src/env.cc
10
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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@
|
|||
#include <unicode/utypes.h>
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "node_metadata.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(_STLP_VENDOR_CSTD)
|
||||
// STLPort doesn't import fpclassify into the std namespace.
|
||||
|
|
@ -218,5 +218,48 @@ void TracedValue::AppendAsTraceFormat(std::string* out) const {
|
|||
*out += root_is_array_ ? ']' : '}';
|
||||
}
|
||||
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> 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<v8::ConvertableToTraceFormat> 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<v8::ConvertableToTraceFormat> ProcessMeta::Cast() const {
|
||||
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();
|
||||
|
||||
return trace_process;
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace node
|
||||
|
|
|
|||
|
|
@ -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 <cstddef>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
namespace node {
|
||||
namespace tracing {
|
||||
|
||||
template <typename T>
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> CastTracedValue(const T& value) {
|
||||
return value.Cast();
|
||||
}
|
||||
|
||||
class EnvironmentArgs {
|
||||
public:
|
||||
EnvironmentArgs(std::span<const std::string> args,
|
||||
std::span<const std::string> exec_args)
|
||||
: args_(args), exec_args_(exec_args) {}
|
||||
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> Cast() const;
|
||||
|
||||
private:
|
||||
std::span<const std::string> args_;
|
||||
std::span<const std::string> 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<v8::ConvertableToTraceFormat> Cast() const;
|
||||
|
||||
private:
|
||||
int64_t execution_async_id_;
|
||||
int64_t trigger_async_id_;
|
||||
};
|
||||
|
||||
class ProcessMeta {
|
||||
public:
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> 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<v8::ConvertableToTraceFormat> 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;
|
||||
|
|
|
|||
|
|
@ -94,3 +94,29 @@ TEST(TracedValue, EscapingArray) {
|
|||
|
||||
EXPECT_EQ(check, string);
|
||||
}
|
||||
|
||||
TEST(TracedValue, EnvironmentArgs) {
|
||||
std::vector<std::string> args{"a", "bb", "ccc"};
|
||||
std::vector<std::string> 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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user