Skip to content

Commit 1a1951f

Browse files
committed
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_); } }; ```
1 parent d12ee6e commit 1a1951f

File tree

6 files changed

+156
-56
lines changed

6 files changed

+156
-56
lines changed

src/async_wrap.cc

+13-15
Original file line numberDiff line numberDiff line change
@@ -602,21 +602,19 @@ void AsyncWrap::AsyncReset(Local<Object> resource, double execution_async_id) {
602602
}
603603

604604
switch (provider_type()) {
605-
#define V(PROVIDER) \
606-
case PROVIDER_ ## PROVIDER: \
607-
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \
608-
TRACING_CATEGORY_NODE1(async_hooks))) { \
609-
auto data = tracing::TracedValue::Create(); \
610-
data->SetInteger("executionAsyncId", \
611-
static_cast<int64_t>(env()->execution_async_id())); \
612-
data->SetInteger("triggerAsyncId", \
613-
static_cast<int64_t>(get_trigger_async_id())); \
614-
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( \
615-
TRACING_CATEGORY_NODE1(async_hooks), \
616-
#PROVIDER, static_cast<int64_t>(get_async_id()), \
617-
"data", std::move(data)); \
618-
} \
619-
break;
605+
#define V(PROVIDER) \
606+
case PROVIDER_##PROVIDER: \
607+
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \
608+
TRACING_CATEGORY_NODE1(async_hooks))) { \
609+
tracing::AsyncWrapArgs data(env()->execution_async_id(), \
610+
get_trigger_async_id()); \
611+
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(async_hooks), \
612+
#PROVIDER, \
613+
static_cast<int64_t>(get_async_id()), \
614+
"data", \
615+
tracing::CastTracedValue(data)); \
616+
} \
617+
break;
620618
NODE_ASYNC_PROVIDER_TYPES(V)
621619
#undef V
622620
default:

src/env.cc

+2-8
Original file line numberDiff line numberDiff line change
@@ -914,18 +914,12 @@ Environment::Environment(IsolateData* isolate_data,
914914

915915
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
916916
TRACING_CATEGORY_NODE1(environment)) != 0) {
917-
auto traced_value = tracing::TracedValue::Create();
918-
traced_value->BeginArray("args");
919-
for (const std::string& arg : args) traced_value->AppendString(arg);
920-
traced_value->EndArray();
921-
traced_value->BeginArray("exec_args");
922-
for (const std::string& arg : exec_args) traced_value->AppendString(arg);
923-
traced_value->EndArray();
917+
tracing::EnvironmentArgs traced_value(args, exec_args);
924918
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(environment),
925919
"Environment",
926920
this,
927921
"args",
928-
std::move(traced_value));
922+
tracing::CastTracedValue(traced_value));
929923
}
930924

931925
if (options_->permission) {

src/node_v8_platform-inl.h

+5-25
Original file line numberDiff line numberDiff line change
@@ -38,31 +38,11 @@ class NodeTraceStateObserver
3838
TRACE_EVENT_METADATA1(
3939
"__metadata", "thread_name", "name", "JavaScriptMainThread");
4040

41-
auto trace_process = tracing::TracedValue::Create();
42-
trace_process->BeginDictionary("versions");
43-
44-
#define V(key) \
45-
trace_process->SetString(#key, per_process::metadata.versions.key.c_str());
46-
47-
NODE_VERSIONS_KEYS(V)
48-
#undef V
49-
50-
trace_process->EndDictionary();
51-
52-
trace_process->SetString("arch", per_process::metadata.arch.c_str());
53-
trace_process->SetString("platform",
54-
per_process::metadata.platform.c_str());
55-
56-
trace_process->BeginDictionary("release");
57-
trace_process->SetString("name",
58-
per_process::metadata.release.name.c_str());
59-
#if NODE_VERSION_IS_LTS
60-
trace_process->SetString("lts", per_process::metadata.release.lts.c_str());
61-
#endif
62-
trace_process->EndDictionary();
63-
TRACE_EVENT_METADATA1(
64-
"__metadata", "node", "process", std::move(trace_process));
65-
41+
tracing::ProcessMeta trace_process;
42+
TRACE_EVENT_METADATA1("__metadata",
43+
"node",
44+
"process",
45+
tracing::CastTracedValue(trace_process));
6646
// This only runs the first time tracing is enabled
6747
controller_->RemoveTraceStateObserver(this);
6848
}

src/tracing/traced_value.cc

+46-3
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
#include <unicode/utypes.h>
1010
#endif
1111

12-
#include <cmath>
13-
#include <cstdio>
1412
#include <sstream>
15-
#include <string>
13+
14+
#include "node_metadata.h"
15+
#include "util.h"
1616

1717
#if defined(_STLP_VENDOR_CSTD)
1818
// STLPort doesn't import fpclassify into the std namespace.
@@ -218,5 +218,48 @@ void TracedValue::AppendAsTraceFormat(std::string* out) const {
218218
*out += root_is_array_ ? ']' : '}';
219219
}
220220

221+
std::unique_ptr<v8::ConvertableToTraceFormat> EnvironmentArgs::Cast() const {
222+
auto traced_value = tracing::TracedValue::Create();
223+
traced_value->BeginArray("args");
224+
for (const std::string& arg : args_) traced_value->AppendString(arg);
225+
traced_value->EndArray();
226+
traced_value->BeginArray("exec_args");
227+
for (const std::string& arg : exec_args_) traced_value->AppendString(arg);
228+
traced_value->EndArray();
229+
return traced_value;
230+
}
231+
232+
std::unique_ptr<v8::ConvertableToTraceFormat> AsyncWrapArgs::Cast() const {
233+
auto data = tracing::TracedValue::Create();
234+
data->SetInteger("executionAsyncId", execution_async_id_);
235+
data->SetInteger("triggerAsyncId", trigger_async_id_);
236+
return data;
237+
}
238+
239+
std::unique_ptr<v8::ConvertableToTraceFormat> ProcessMeta::Cast() const {
240+
auto trace_process = tracing::TracedValue::Create();
241+
trace_process->BeginDictionary("versions");
242+
243+
#define V(key) \
244+
trace_process->SetString(#key, per_process::metadata.versions.key.c_str());
245+
246+
NODE_VERSIONS_KEYS(V)
247+
#undef V
248+
249+
trace_process->EndDictionary();
250+
251+
trace_process->SetString("arch", per_process::metadata.arch.c_str());
252+
trace_process->SetString("platform", per_process::metadata.platform.c_str());
253+
254+
trace_process->BeginDictionary("release");
255+
trace_process->SetString("name", per_process::metadata.release.name.c_str());
256+
#if NODE_VERSION_IS_LTS
257+
trace_process->SetString("lts", per_process::metadata.release.lts.c_str());
258+
#endif
259+
trace_process->EndDictionary();
260+
261+
return trace_process;
262+
}
263+
221264
} // namespace tracing
222265
} // namespace node

src/tracing/traced_value.h

+64-5
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,76 @@
55
#ifndef SRC_TRACING_TRACED_VALUE_H_
66
#define SRC_TRACING_TRACED_VALUE_H_
77

8-
#include "node.h"
9-
#include "util.h"
10-
#include "v8.h"
8+
#include "v8-platform.h"
119

12-
#include <cstddef>
13-
#include <memory>
10+
#include <cstdint>
11+
#include <span>
1412
#include <string>
1513

1614
namespace node {
1715
namespace tracing {
1816

17+
template <typename T>
18+
std::unique_ptr<v8::ConvertableToTraceFormat> CastTracedValue(const T& value) {
19+
return value.Cast();
20+
}
21+
22+
class EnvironmentArgs {
23+
public:
24+
EnvironmentArgs(std::span<const std::string> args,
25+
std::span<const std::string> exec_args)
26+
: args_(args), exec_args_(exec_args) {}
27+
28+
std::unique_ptr<v8::ConvertableToTraceFormat> Cast() const;
29+
30+
private:
31+
std::span<const std::string> args_;
32+
std::span<const std::string> exec_args_;
33+
};
34+
35+
class AsyncWrapArgs {
36+
public:
37+
AsyncWrapArgs(int64_t execution_async_id, int64_t trigger_async_id)
38+
: execution_async_id_(execution_async_id),
39+
trigger_async_id_(trigger_async_id) {}
40+
41+
std::unique_ptr<v8::ConvertableToTraceFormat> Cast() const;
42+
43+
private:
44+
int64_t execution_async_id_;
45+
int64_t trigger_async_id_;
46+
};
47+
48+
class ProcessMeta {
49+
public:
50+
std::unique_ptr<v8::ConvertableToTraceFormat> Cast() const;
51+
};
52+
53+
// Do not use this class directly. Define a custom structured class to provide
54+
// a conversion method so that the class can be used with both V8 legacy
55+
// trace API and perfetto API.
56+
//
57+
// These classes provide a JSON-inspired way to write structed data into traces.
58+
//
59+
// To define how a custom class should be written into the trace, users should
60+
// define one of the two following functions:
61+
// - Foo::Cast(TracedValue) const
62+
// (preferred for code which depends on perfetto directly)
63+
//
64+
// After defining a conversion method, the object can be used as a
65+
// TRACE_EVENT argument:
66+
//
67+
// Foo foo;
68+
// TRACE_EVENT("cat", "Event", "arg", CastTracedValue(foo));
69+
//
70+
// class Foo {
71+
// std::unique_ptr<v8::ConvertableToTraceFormat> Cast() const {
72+
// auto traced_value = tracing::TracedValue::Create();
73+
// dict->SetInteger("key", 42);
74+
// dict->SetString("foo", "bar");
75+
// return traced_value;
76+
// }
77+
// }
1978
class TracedValue : public v8::ConvertableToTraceFormat {
2079
public:
2180
~TracedValue() override = default;

test/cctest/test_traced_value.cc

+26
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,29 @@ TEST(TracedValue, EscapingArray) {
9494

9595
EXPECT_EQ(check, string);
9696
}
97+
98+
TEST(TracedValue, EnvironmentArgs) {
99+
std::vector<std::string> args{"a", "bb", "ccc"};
100+
std::vector<std::string> exec_args{"--inspect", "--a-long-arg"};
101+
node::tracing::EnvironmentArgs env_args(args, exec_args);
102+
103+
std::string string;
104+
env_args.Cast()->AppendAsTraceFormat(&string);
105+
106+
static const char* check = "{\"args\":[\"a\",\"bb\",\"ccc\"],"
107+
"\"exec_args\":[\"--inspect\",\"--a-long-arg\"]}";
108+
109+
EXPECT_EQ(check, string);
110+
}
111+
112+
TEST(TracedValue, AsyncWrapArgs) {
113+
node::tracing::AsyncWrapArgs aw_args(1, 1);
114+
115+
std::string string;
116+
aw_args.Cast()->AppendAsTraceFormat(&string);
117+
118+
static const char* check = "{\"executionAsyncId\":1,"
119+
"\"triggerAsyncId\":1}";
120+
121+
EXPECT_EQ(check, string);
122+
}

0 commit comments

Comments
 (0)