1#ifndef SRC_NODE_V8_PLATFORM_INL_H_
2#define SRC_NODE_V8_PLATFORM_INL_H_
3
4#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5
6#include <memory>
7#include <string_view>
8
9#include "env-inl.h"
10#include "node.h"
11#include "node_metadata.h"
12#include "node_platform.h"
13#include "node_options.h"
14#include "tracing/node_trace_writer.h"
15#include "tracing/trace_event.h"
16#include "tracing/traced_value.h"
17#include "util.h"
18
19namespace node {
20
21// Ensures that __metadata trace events are only emitted
22// when tracing is enabled.
23class NodeTraceStateObserver
24    : public v8::TracingController::TraceStateObserver {
25 public:
26  inline void OnTraceEnabled() override {
27    std::string title = GetProcessTitle("");
28    if (!title.empty()) {
29      // Only emit the metadata event if the title can be retrieved
30      // successfully. Ignore it otherwise.
31      TRACE_EVENT_METADATA1(
32          "__metadata", "process_name", "name", TRACE_STR_COPY(title.c_str()));
33    }
34    TRACE_EVENT_METADATA1("__metadata",
35                          "version",
36                          "node",
37                          per_process::metadata.versions.node.c_str());
38    TRACE_EVENT_METADATA1(
39        "__metadata", "thread_name", "name", "JavaScriptMainThread");
40
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
66    // This only runs the first time tracing is enabled
67    controller_->RemoveTraceStateObserver(this);
68  }
69
70  inline void OnTraceDisabled() override {
71    // Do nothing here. This should never be called because the
72    // observer removes itself when OnTraceEnabled() is called.
73    UNREACHABLE();
74  }
75
76  explicit NodeTraceStateObserver(v8::TracingController* controller)
77      : controller_(controller) {}
78  ~NodeTraceStateObserver() override = default;
79
80 private:
81  v8::TracingController* controller_;
82};
83
84struct V8Platform {
85  bool initialized_ = false;
86
87#if NODE_USE_V8_PLATFORM
88  inline void Initialize(int thread_pool_size) {
89    CHECK(!initialized_);
90    initialized_ = true;
91    tracing_agent_ = std::make_unique<tracing::Agent>();
92    node::tracing::TraceEventHelper::SetAgent(tracing_agent_.get());
93    node::tracing::TracingController* controller =
94        tracing_agent_->GetTracingController();
95    trace_state_observer_ =
96        std::make_unique<NodeTraceStateObserver>(controller);
97    controller->AddTraceStateObserver(trace_state_observer_.get());
98    tracing_file_writer_ = tracing_agent_->DefaultHandle();
99    // Only start the tracing agent if we enabled any tracing categories.
100    if (!per_process::cli_options->trace_event_categories.empty()) {
101      StartTracingAgent();
102    }
103    // Tracing must be initialized before platform threads are created.
104    platform_ = new NodePlatform(thread_pool_size, controller);
105    v8::V8::InitializePlatform(platform_);
106  }
107  // Make sure V8Platform don not call into Libuv threadpool,
108  // see DefaultProcessExitHandlerInternal in environment.cc
109  inline void Dispose() {
110    if (!initialized_)
111      return;
112    initialized_ = false;
113    node::tracing::TraceEventHelper::SetAgent(nullptr);
114    StopTracingAgent();
115    platform_->Shutdown();
116    delete platform_;
117    platform_ = nullptr;
118    // Destroy tracing after the platform (and platform threads) have been
119    // stopped.
120    tracing_agent_.reset(nullptr);
121    // The observer remove itself in OnTraceEnabled
122    trace_state_observer_.reset(nullptr);
123  }
124
125  inline void DrainVMTasks(v8::Isolate* isolate) {
126    platform_->DrainTasks(isolate);
127  }
128
129  inline void StartTracingAgent() {
130    constexpr auto convert_to_set =
131        [](std::vector<std::string_view> categories) -> std::set<std::string> {
132      std::set<std::string> out;
133      for (const auto& s : categories) {
134        out.emplace(s);
135      }
136      return out;
137    };
138    // Attach a new NodeTraceWriter only if this function hasn't been called
139    // before.
140    if (tracing_file_writer_.IsDefaultHandle()) {
141      using std::string_view_literals::operator""sv;
142      const std::vector<std::string_view> categories =
143          SplitString(per_process::cli_options->trace_event_categories, ","sv);
144
145      tracing_file_writer_ = tracing_agent_->AddClient(
146          convert_to_set(categories),
147          std::unique_ptr<tracing::AsyncTraceWriter>(
148              new tracing::NodeTraceWriter(
149                  per_process::cli_options->trace_event_file_pattern)),
150          tracing::Agent::kUseDefaultCategories);
151    }
152  }
153
154  inline void StopTracingAgent() { tracing_file_writer_.reset(); }
155
156  inline tracing::AgentWriterHandle* GetTracingAgentWriter() {
157    return &tracing_file_writer_;
158  }
159
160  inline NodePlatform* Platform() { return platform_; }
161
162  std::unique_ptr<NodeTraceStateObserver> trace_state_observer_;
163  std::unique_ptr<tracing::Agent> tracing_agent_;
164  tracing::AgentWriterHandle tracing_file_writer_;
165  NodePlatform* platform_;
166#else   // !NODE_USE_V8_PLATFORM
167  inline void Initialize(int thread_pool_size) {}
168  inline void Dispose() {}
169  inline void DrainVMTasks(v8::Isolate* isolate) {}
170  inline void StartTracingAgent() {
171    if (!per_process::cli_options->trace_event_categories.empty()) {
172      fprintf(stderr,
173              "Node compiled with NODE_USE_V8_PLATFORM=0, "
174              "so event tracing is not available.\n");
175    }
176  }
177  inline void StopTracingAgent() {}
178
179  inline tracing::AgentWriterHandle* GetTracingAgentWriter() { return nullptr; }
180
181  inline NodePlatform* Platform() { return nullptr; }
182#endif  // !NODE_USE_V8_PLATFORM
183};
184
185namespace per_process {
186extern struct V8Platform v8_platform;
187}
188
189inline void StartTracingAgent() {
190  return per_process::v8_platform.StartTracingAgent();
191}
192
193inline tracing::AgentWriterHandle* GetTracingAgentWriter() {
194  return per_process::v8_platform.GetTracingAgentWriter();
195}
196
197inline void DisposePlatform() {
198  per_process::v8_platform.Dispose();
199}
200
201}  // namespace node
202
203#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
204
205#endif  // SRC_NODE_V8_PLATFORM_INL_H_
206