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: |
19 |
|
7088 |
explicit DeletableFrontendWrapper( |
20 |
|
|
std::weak_ptr<NodeTracing::Frontend> frontend) |
21 |
|
7088 |
: frontend_(frontend) {} |
22 |
|
|
|
23 |
|
|
// This should only be called from the main thread, meaning frontend should |
24 |
|
|
// not be destroyed concurrently. |
25 |
|
4 |
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: |
33 |
|
7088 |
CreateFrontendWrapperRequest(int object_id, |
34 |
|
|
std::weak_ptr<NodeTracing::Frontend> frontend) |
35 |
|
7088 |
: object_id_(object_id) { |
36 |
|
7088 |
frontend_wrapper_ = std::make_unique<DeletableFrontendWrapper>(frontend); |
37 |
|
7088 |
} |
38 |
|
|
|
39 |
|
7088 |
void Call(MainThreadInterface* thread) override { |
40 |
|
7088 |
thread->AddObject(object_id_, std::move(frontend_wrapper_)); |
41 |
|
7088 |
} |
42 |
|
|
|
43 |
|
|
private: |
44 |
|
|
int object_id_; |
45 |
|
|
std::unique_ptr<DeletableFrontendWrapper> frontend_wrapper_; |
46 |
|
|
}; |
47 |
|
|
|
48 |
|
|
class DestroyFrontendWrapperRequest : public Request { |
49 |
|
|
public: |
50 |
|
6538 |
explicit DestroyFrontendWrapperRequest(int object_id) |
51 |
|
6538 |
: object_id_(object_id) {} |
52 |
|
|
|
53 |
|
930 |
void Call(MainThreadInterface* thread) override { |
54 |
|
930 |
thread->RemoveObject(object_id_); |
55 |
|
930 |
} |
56 |
|
|
|
57 |
|
|
private: |
58 |
|
|
int object_id_; |
59 |
|
|
}; |
60 |
|
|
|
61 |
|
|
class SendMessageRequest : public Request { |
62 |
|
|
public: |
63 |
|
4 |
explicit SendMessageRequest(int object_id, const std::string& message) |
64 |
|
4 |
: object_id_(object_id), message_(message) {} |
65 |
|
|
|
66 |
|
4 |
void Call(MainThreadInterface* thread) override { |
67 |
|
|
DeletableFrontendWrapper* frontend_wrapper = |
68 |
|
|
static_cast<DeletableFrontendWrapper*>( |
69 |
|
4 |
thread->GetObjectIfExists(object_id_)); |
70 |
✗✓ |
4 |
if (frontend_wrapper == nullptr) return; |
71 |
|
4 |
auto frontend = frontend_wrapper->get(); |
72 |
✓✗ |
4 |
if (frontend != nullptr) { |
73 |
|
4 |
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: |
84 |
|
2 |
explicit InspectorTraceWriter(int frontend_object_id, |
85 |
|
|
std::shared_ptr<MainThreadHandle> main_thread) |
86 |
|
2 |
: frontend_object_id_(frontend_object_id), main_thread_(main_thread) {} |
87 |
|
|
|
88 |
|
93 |
void AppendTraceEvent( |
89 |
|
|
v8::platform::tracing::TraceObject* trace_event) override { |
90 |
✓✓ |
93 |
if (!json_writer_) |
91 |
|
4 |
json_writer_.reset(TraceWriter::CreateJSONTraceWriter(stream_, "value")); |
92 |
|
93 |
json_writer_->AppendTraceEvent(trace_event); |
93 |
|
93 |
} |
94 |
|
|
|
95 |
|
4 |
void Flush(bool) override { |
96 |
✗✓ |
4 |
if (!json_writer_) |
97 |
|
|
return; |
98 |
|
4 |
json_writer_.reset(); |
99 |
|
|
std::ostringstream result( |
100 |
|
|
"{\"method\":\"NodeTracing.dataCollected\",\"params\":", |
101 |
|
8 |
std::ostringstream::ate); |
102 |
|
4 |
result << stream_.str(); |
103 |
|
4 |
result << "}"; |
104 |
|
8 |
main_thread_->Post(std::make_unique<SendMessageRequest>(frontend_object_id_, |
105 |
|
8 |
result.str())); |
106 |
|
4 |
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 |
|
|
|
117 |
|
7088 |
TracingAgent::TracingAgent(Environment* env, |
118 |
|
7088 |
std::shared_ptr<MainThreadHandle> main_thread) |
119 |
|
7088 |
: env_(env), main_thread_(main_thread) {} |
120 |
|
|
|
121 |
|
26152 |
TracingAgent::~TracingAgent() { |
122 |
|
13076 |
trace_writer_.reset(); |
123 |
|
26152 |
main_thread_->Post( |
124 |
|
26152 |
std::make_unique<DestroyFrontendWrapperRequest>(frontend_object_id_)); |
125 |
|
26152 |
} |
126 |
|
|
|
127 |
|
7088 |
void TracingAgent::Wire(UberDispatcher* dispatcher) { |
128 |
|
|
// Note that frontend is still owned by TracingAgent |
129 |
|
7088 |
frontend_ = std::make_shared<NodeTracing::Frontend>(dispatcher->channel()); |
130 |
|
7088 |
frontend_object_id_ = main_thread_->newObjectId(); |
131 |
|
14176 |
main_thread_->Post(std::make_unique<CreateFrontendWrapperRequest>( |
132 |
|
7088 |
frontend_object_id_, frontend_)); |
133 |
|
7088 |
NodeTracing::Dispatcher::wire(dispatcher, this); |
134 |
|
7088 |
} |
135 |
|
|
|
136 |
|
3 |
DispatchResponse TracingAgent::start( |
137 |
|
|
std::unique_ptr<protocol::NodeTracing::TraceConfig> traceConfig) { |
138 |
✗✓ |
3 |
if (!trace_writer_.empty()) { |
139 |
|
|
return DispatchResponse::Error( |
140 |
|
|
"Call NodeTracing::end to stop tracing before updating the config"); |
141 |
|
|
} |
142 |
✓✓ |
3 |
if (!env_->owns_process_state()) { |
143 |
|
|
return DispatchResponse::Error( |
144 |
|
1 |
"Tracing properties can only be changed through main thread sessions"); |
145 |
|
|
} |
146 |
|
|
|
147 |
|
4 |
std::set<std::string> categories_set; |
148 |
|
|
protocol::Array<std::string>* categories = |
149 |
|
2 |
traceConfig->getIncludedCategories(); |
150 |
✓✓ |
4 |
for (size_t i = 0; i < categories->length(); i++) |
151 |
|
2 |
categories_set.insert(categories->get(i)); |
152 |
|
|
|
153 |
✗✓ |
2 |
if (categories_set.empty()) |
154 |
|
|
return DispatchResponse::Error("At least one category should be enabled"); |
155 |
|
|
|
156 |
|
2 |
tracing::AgentWriterHandle* writer = GetTracingAgentWriter(); |
157 |
✓✗ |
2 |
if (writer != nullptr) { |
158 |
|
|
trace_writer_ = |
159 |
|
6 |
writer->agent()->AddClient(categories_set, |
160 |
|
2 |
std::make_unique<InspectorTraceWriter>( |
161 |
|
2 |
frontend_object_id_, main_thread_), |
162 |
|
2 |
tracing::Agent::kIgnoreDefaultCategories); |
163 |
|
|
} |
164 |
|
2 |
return DispatchResponse::OK(); |
165 |
|
|
} |
166 |
|
|
|
167 |
|
2 |
DispatchResponse TracingAgent::stop() { |
168 |
|
2 |
trace_writer_.reset(); |
169 |
|
2 |
frontend_->tracingComplete(); |
170 |
|
2 |
return DispatchResponse::OK(); |
171 |
|
|
} |
172 |
|
|
|
173 |
|
1 |
DispatchResponse TracingAgent::getCategories( |
174 |
|
|
std::unique_ptr<protocol::Array<String>>* categories) { |
175 |
|
1 |
*categories = Array<String>::create(); |
176 |
|
1 |
categories->get()->addItem("node"); |
177 |
|
1 |
categories->get()->addItem("node.async"); |
178 |
|
1 |
categories->get()->addItem("node.bootstrap"); |
179 |
|
1 |
categories->get()->addItem("node.fs.sync"); |
180 |
|
1 |
categories->get()->addItem("node.perf"); |
181 |
|
1 |
categories->get()->addItem("node.perf.usertiming"); |
182 |
|
1 |
categories->get()->addItem("node.perf.timerify"); |
183 |
|
1 |
categories->get()->addItem("v8"); |
184 |
|
1 |
return DispatchResponse::OK(); |
185 |
|
|
} |
186 |
|
|
|
187 |
|
|
} // namespace protocol |
188 |
|
|
} // namespace inspector |
189 |
|
|
} // namespace node |