1#include "tracing_agent.h"
2#include "main_thread_interface.h"
3#include "node_internals.h"
4#include "node_v8_platform-inl.h"
5#include "v8.h"
6
7#include <set>
8#include <sstream>
9
10namespace node {
11namespace inspector {
12namespace protocol {
13
14namespace {
15using v8::platform::tracing::TraceWriter;
16
17class DeletableFrontendWrapper : public Deletable {
18 public:
19  explicit DeletableFrontendWrapper(
20      std::weak_ptr<NodeTracing::Frontend> frontend)
21      : frontend_(frontend) {}
22
23  // This should only be called from the main thread, meaning frontend should
24  // not be destroyed concurrently.
25  NodeTracing::Frontend* get() { return frontend_.lock().get(); }
26
27 private:
28  std::weak_ptr<NodeTracing::Frontend> frontend_;
29};
30
31class CreateFrontendWrapperRequest : public Request {
32 public:
33  CreateFrontendWrapperRequest(int object_id,
34                               std::weak_ptr<NodeTracing::Frontend> frontend)
35      : object_id_(object_id) {
36    frontend_wrapper_ = std::make_unique<DeletableFrontendWrapper>(frontend);
37  }
38
39  void Call(MainThreadInterface* thread) override {
40    thread->AddObject(object_id_, std::move(frontend_wrapper_));
41  }
42
43 private:
44  int object_id_;
45  std::unique_ptr<DeletableFrontendWrapper> frontend_wrapper_;
46};
47
48class DestroyFrontendWrapperRequest : public Request {
49 public:
50  explicit DestroyFrontendWrapperRequest(int object_id)
51      : object_id_(object_id) {}
52
53  void Call(MainThreadInterface* thread) override {
54    thread->RemoveObject(object_id_);
55  }
56
57 private:
58  int object_id_;
59};
60
61class SendMessageRequest : public Request {
62 public:
63  explicit SendMessageRequest(int object_id, const std::string& message)
64      : object_id_(object_id), message_(message) {}
65
66  void Call(MainThreadInterface* thread) override {
67    DeletableFrontendWrapper* frontend_wrapper =
68        static_cast<DeletableFrontendWrapper*>(
69            thread->GetObjectIfExists(object_id_));
70    if (frontend_wrapper == nullptr) return;
71    auto frontend = frontend_wrapper->get();
72    if (frontend != nullptr) {
73      frontend->sendRawJSONNotification(message_);
74    }
75  }
76
77 private:
78  int object_id_;
79  std::string message_;
80};
81
82class InspectorTraceWriter : public node::tracing::AsyncTraceWriter {
83 public:
84  explicit InspectorTraceWriter(int frontend_object_id,
85                                std::shared_ptr<MainThreadHandle> main_thread)
86      : frontend_object_id_(frontend_object_id), main_thread_(main_thread) {}
87
88  void AppendTraceEvent(
89      v8::platform::tracing::TraceObject* trace_event) override {
90    if (!json_writer_)
91      json_writer_.reset(TraceWriter::CreateJSONTraceWriter(stream_, "value"));
92    json_writer_->AppendTraceEvent(trace_event);
93  }
94
95  void Flush(bool) override {
96    if (!json_writer_)
97      return;
98    json_writer_.reset();
99    std::ostringstream result(
100        "{\"method\":\"NodeTracing.dataCollected\",\"params\":",
101        std::ostringstream::ate);
102    result << stream_.str();
103    result << "}";
104    main_thread_->Post(std::make_unique<SendMessageRequest>(frontend_object_id_,
105                                                            result.str()));
106    stream_.str("");
107  }
108
109 private:
110  std::unique_ptr<TraceWriter> json_writer_;
111  std::ostringstream stream_;
112  int frontend_object_id_;
113  std::shared_ptr<MainThreadHandle> main_thread_;
114};
115}  // namespace
116
117TracingAgent::TracingAgent(Environment* env,
118                           std::shared_ptr<MainThreadHandle> main_thread)
119    : env_(env), main_thread_(main_thread) {}
120
121TracingAgent::~TracingAgent() {
122  trace_writer_.reset();
123  main_thread_->Post(
124      std::make_unique<DestroyFrontendWrapperRequest>(frontend_object_id_));
125}
126
127void TracingAgent::Wire(UberDispatcher* dispatcher) {
128  // Note that frontend is still owned by TracingAgent
129  frontend_ = std::make_shared<NodeTracing::Frontend>(dispatcher->channel());
130  frontend_object_id_ = main_thread_->newObjectId();
131  main_thread_->Post(std::make_unique<CreateFrontendWrapperRequest>(
132      frontend_object_id_, frontend_));
133  NodeTracing::Dispatcher::wire(dispatcher, this);
134}
135
136DispatchResponse TracingAgent::start(
137    std::unique_ptr<protocol::NodeTracing::TraceConfig> traceConfig) {
138  if (!trace_writer_.empty()) {
139    return DispatchResponse::Error(
140        "Call NodeTracing::end to stop tracing before updating the config");
141  }
142  if (!env_->owns_process_state()) {
143    return DispatchResponse::Error(
144        "Tracing properties can only be changed through main thread sessions");
145  }
146
147  std::set<std::string> categories_set;
148  protocol::Array<std::string>* categories =
149      traceConfig->getIncludedCategories();
150  for (size_t i = 0; i < categories->length(); i++)
151    categories_set.insert(categories->get(i));
152
153  if (categories_set.empty())
154    return DispatchResponse::Error("At least one category should be enabled");
155
156  tracing::AgentWriterHandle* writer = GetTracingAgentWriter();
157  if (writer != nullptr) {
158    trace_writer_ =
159        writer->agent()->AddClient(categories_set,
160                                   std::make_unique<InspectorTraceWriter>(
161                                       frontend_object_id_, main_thread_),
162                                   tracing::Agent::kIgnoreDefaultCategories);
163  }
164  return DispatchResponse::OK();
165}
166
167DispatchResponse TracingAgent::stop() {
168  trace_writer_.reset();
169  frontend_->tracingComplete();
170  return DispatchResponse::OK();
171}
172
173DispatchResponse TracingAgent::getCategories(
174    std::unique_ptr<protocol::Array<String>>* categories) {
175  *categories = Array<String>::create();
176  protocol::Array<String>* categories_list = categories->get();
177  // In alphabetical order
178  categories_list->addItem("node");
179  categories_list->addItem("node.async_hooks");
180  categories_list->addItem("node.bootstrap");
181  categories_list->addItem("node.console");
182  categories_list->addItem("node.dns.native");
183  categories_list->addItem("node.environment");
184  categories_list->addItem("node.fs.async");
185  categories_list->addItem("node.fs.sync");
186  categories_list->addItem("node.fs_dir.async");
187  categories_list->addItem("node.fs_dir.sync");
188  categories_list->addItem("node.http");
189  categories_list->addItem("node.net.native");
190  categories_list->addItem("node.perf");
191  categories_list->addItem("node.perf.timerify");
192  categories_list->addItem("node.perf.usertiming");
193  categories_list->addItem("node.promises.rejections");
194  categories_list->addItem("node.threadpoolwork.async");
195  categories_list->addItem("node.threadpoolwork.sync");
196  categories_list->addItem("node.vm.script");
197  categories_list->addItem("v8");
198  return DispatchResponse::OK();
199}
200
201}  // namespace protocol
202}  // namespace inspector
203}  // namespace node
204