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
10 namespace node {
11 namespace inspector {
12 namespace protocol {
13
14 namespace {
15 using v8::platform::tracing::TraceWriter;
16
17 class DeletableFrontendWrapper : public Deletable {
18 public:
DeletableFrontendWrapper(std::weak_ptr<NodeTracing::Frontend> frontend)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.
get()25 NodeTracing::Frontend* get() { return frontend_.lock().get(); }
26
27 private:
28 std::weak_ptr<NodeTracing::Frontend> frontend_;
29 };
30
31 class CreateFrontendWrapperRequest : public Request {
32 public:
CreateFrontendWrapperRequest(int object_id,std::weak_ptr<NodeTracing::Frontend> frontend)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
Call(MainThreadInterface * thread)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
48 class DestroyFrontendWrapperRequest : public Request {
49 public:
DestroyFrontendWrapperRequest(int object_id)50 explicit DestroyFrontendWrapperRequest(int object_id)
51 : object_id_(object_id) {}
52
Call(MainThreadInterface * thread)53 void Call(MainThreadInterface* thread) override {
54 thread->RemoveObject(object_id_);
55 }
56
57 private:
58 int object_id_;
59 };
60
61 class SendMessageRequest : public Request {
62 public:
SendMessageRequest(int object_id,const std::string & message)63 explicit SendMessageRequest(int object_id, const std::string& message)
64 : object_id_(object_id), message_(message) {}
65
Call(MainThreadInterface * thread)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
82 class InspectorTraceWriter : public node::tracing::AsyncTraceWriter {
83 public:
InspectorTraceWriter(int frontend_object_id,std::shared_ptr<MainThreadHandle> main_thread)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
AppendTraceEvent(v8::platform::tracing::TraceObject * trace_event)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
Flush(bool)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
TracingAgent(Environment * env,std::shared_ptr<MainThreadHandle> main_thread)117 TracingAgent::TracingAgent(Environment* env,
118 std::shared_ptr<MainThreadHandle> main_thread)
119 : env_(env), main_thread_(main_thread) {}
120
~TracingAgent()121 TracingAgent::~TracingAgent() {
122 trace_writer_.reset();
123 main_thread_->Post(
124 std::make_unique<DestroyFrontendWrapperRequest>(frontend_object_id_));
125 }
126
Wire(UberDispatcher * dispatcher)127 void 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
start(std::unique_ptr<protocol::NodeTracing::TraceConfig> traceConfig)136 DispatchResponse 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
stop()167 DispatchResponse TracingAgent::stop() {
168 trace_writer_.reset();
169 frontend_->tracingComplete();
170 return DispatchResponse::OK();
171 }
172
getCategories(std::unique_ptr<protocol::Array<String>> * categories)173 DispatchResponse TracingAgent::getCategories(
174 std::unique_ptr<protocol::Array<String>>* categories) {
175 *categories = Array<String>::create();
176 categories->get()->addItem("node");
177 categories->get()->addItem("node.async");
178 categories->get()->addItem("node.bootstrap");
179 categories->get()->addItem("node.fs.sync");
180 categories->get()->addItem("node.perf");
181 categories->get()->addItem("node.perf.usertiming");
182 categories->get()->addItem("node.perf.timerify");
183 categories->get()->addItem("v8");
184 return DispatchResponse::OK();
185 }
186
187 } // namespace protocol
188 } // namespace inspector
189 } // namespace node
190