1 |
|
|
#include "tracing/agent.h" |
2 |
|
|
|
3 |
|
|
#include <string> |
4 |
|
|
#include "trace_event.h" |
5 |
|
|
#include "tracing/node_trace_buffer.h" |
6 |
|
|
#include "debug_utils-inl.h" |
7 |
|
|
#include "env-inl.h" |
8 |
|
|
|
9 |
|
|
namespace node { |
10 |
|
|
namespace tracing { |
11 |
|
|
|
12 |
|
|
class Agent::ScopedSuspendTracing { |
13 |
|
|
public: |
14 |
|
138 |
ScopedSuspendTracing(TracingController* controller, Agent* agent, |
15 |
|
|
bool do_suspend = true) |
16 |
✓✗ |
138 |
: controller_(controller), agent_(do_suspend ? agent : nullptr) { |
17 |
✓✗ |
138 |
if (do_suspend) { |
18 |
✗✓ |
138 |
CHECK(agent_->started_); |
19 |
|
138 |
controller->StopTracing(); |
20 |
|
|
} |
21 |
|
138 |
} |
22 |
|
|
|
23 |
|
138 |
~ScopedSuspendTracing() { |
24 |
✗✓ |
138 |
if (agent_ == nullptr) return; |
25 |
|
138 |
TraceConfig* config = agent_->CreateTraceConfig(); |
26 |
✓✓ |
138 |
if (config != nullptr) { |
27 |
|
136 |
controller_->StartTracing(config); |
28 |
|
|
} |
29 |
|
138 |
} |
30 |
|
|
|
31 |
|
|
private: |
32 |
|
|
TracingController* controller_; |
33 |
|
|
Agent* agent_; |
34 |
|
|
}; |
35 |
|
|
|
36 |
|
|
namespace { |
37 |
|
|
|
38 |
|
153 |
std::set<std::string> flatten( |
39 |
|
|
const std::unordered_map<int, std::multiset<std::string>>& map) { |
40 |
|
153 |
std::set<std::string> result; |
41 |
✓✓ |
399 |
for (const auto& id_value : map) |
42 |
|
246 |
result.insert(id_value.second.begin(), id_value.second.end()); |
43 |
|
153 |
return result; |
44 |
|
|
} |
45 |
|
|
|
46 |
|
|
} // namespace |
47 |
|
|
|
48 |
|
|
using v8::platform::tracing::TraceConfig; |
49 |
|
|
using v8::platform::tracing::TraceWriter; |
50 |
|
|
using std::string; |
51 |
|
|
|
52 |
|
5208 |
Agent::Agent() : tracing_controller_(new TracingController()) { |
53 |
|
5208 |
tracing_controller_->Initialize(nullptr); |
54 |
|
|
|
55 |
✗✓ |
5208 |
CHECK_EQ(uv_loop_init(&tracing_loop_), 0); |
56 |
✗✓ |
5264 |
CHECK_EQ(uv_async_init(&tracing_loop_, |
57 |
|
|
&initialize_writer_async_, |
58 |
|
|
[](uv_async_t* async) { |
59 |
|
|
Agent* agent = ContainerOf(&Agent::initialize_writer_async_, async); |
60 |
|
|
agent->InitializeWritersOnThread(); |
61 |
|
|
}), 0); |
62 |
|
5208 |
uv_unref(reinterpret_cast<uv_handle_t*>(&initialize_writer_async_)); |
63 |
|
5208 |
} |
64 |
|
|
|
65 |
|
56 |
void Agent::InitializeWritersOnThread() { |
66 |
|
112 |
Mutex::ScopedLock lock(initialize_writer_mutex_); |
67 |
✓✓ |
112 |
while (!to_be_initialized_.empty()) { |
68 |
|
56 |
AsyncTraceWriter* head = *to_be_initialized_.begin(); |
69 |
|
56 |
head->InitializeOnThread(&tracing_loop_); |
70 |
|
56 |
to_be_initialized_.erase(head); |
71 |
|
|
} |
72 |
|
56 |
initialize_writer_condvar_.Broadcast(lock); |
73 |
|
56 |
} |
74 |
|
|
|
75 |
|
5208 |
Agent::~Agent() { |
76 |
|
5208 |
categories_.clear(); |
77 |
|
5208 |
writers_.clear(); |
78 |
|
|
|
79 |
|
5208 |
StopTracing(); |
80 |
|
|
|
81 |
|
5208 |
uv_close(reinterpret_cast<uv_handle_t*>(&initialize_writer_async_), nullptr); |
82 |
|
5208 |
uv_run(&tracing_loop_, UV_RUN_ONCE); |
83 |
|
5208 |
CheckedUvLoopClose(&tracing_loop_); |
84 |
|
5208 |
} |
85 |
|
|
|
86 |
|
56 |
void Agent::Start() { |
87 |
✗✓ |
56 |
if (started_) |
88 |
|
|
return; |
89 |
|
|
|
90 |
|
|
NodeTraceBuffer* trace_buffer_ = new NodeTraceBuffer( |
91 |
|
56 |
NodeTraceBuffer::kBufferChunks, this, &tracing_loop_); |
92 |
|
56 |
tracing_controller_->Initialize(trace_buffer_); |
93 |
|
|
|
94 |
|
|
// This thread should be created *after* async handles are created |
95 |
|
|
// (within NodeTraceWriter and NodeTraceBuffer constructors). |
96 |
|
|
// Otherwise the thread could shut down prematurely. |
97 |
✗✓ |
112 |
CHECK_EQ(0, uv_thread_create(&thread_, [](void* arg) { |
98 |
|
|
Agent* agent = static_cast<Agent*>(arg); |
99 |
|
|
uv_run(&agent->tracing_loop_, UV_RUN_DEFAULT); |
100 |
|
|
}, this)); |
101 |
|
56 |
started_ = true; |
102 |
|
|
} |
103 |
|
|
|
104 |
|
56 |
AgentWriterHandle Agent::AddClient( |
105 |
|
|
const std::set<std::string>& categories, |
106 |
|
|
std::unique_ptr<AsyncTraceWriter> writer, |
107 |
|
|
enum UseDefaultCategoryMode mode) { |
108 |
|
56 |
Start(); |
109 |
|
|
|
110 |
|
56 |
const std::set<std::string>* use_categories = &categories; |
111 |
|
|
|
112 |
|
112 |
std::set<std::string> categories_with_default; |
113 |
✓✓ |
56 |
if (mode == kUseDefaultCategories) { |
114 |
|
54 |
categories_with_default.insert(categories.begin(), categories.end()); |
115 |
|
54 |
categories_with_default.insert(categories_[kDefaultHandleId].begin(), |
116 |
|
54 |
categories_[kDefaultHandleId].end()); |
117 |
|
54 |
use_categories = &categories_with_default; |
118 |
|
|
} |
119 |
|
|
|
120 |
|
112 |
ScopedSuspendTracing suspend(tracing_controller_.get(), this); |
121 |
|
56 |
int id = next_writer_id_++; |
122 |
|
56 |
AsyncTraceWriter* raw = writer.get(); |
123 |
|
56 |
writers_[id] = std::move(writer); |
124 |
|
56 |
categories_[id] = { use_categories->begin(), use_categories->end() }; |
125 |
|
|
|
126 |
|
|
{ |
127 |
|
112 |
Mutex::ScopedLock lock(initialize_writer_mutex_); |
128 |
|
56 |
to_be_initialized_.insert(raw); |
129 |
|
56 |
uv_async_send(&initialize_writer_async_); |
130 |
✓✓ |
112 |
while (to_be_initialized_.count(raw) > 0) |
131 |
|
56 |
initialize_writer_condvar_.Wait(lock); |
132 |
|
|
} |
133 |
|
|
|
134 |
|
56 |
return AgentWriterHandle(this, id); |
135 |
|
|
} |
136 |
|
|
|
137 |
|
5207 |
AgentWriterHandle Agent::DefaultHandle() { |
138 |
|
5207 |
return AgentWriterHandle(this, kDefaultHandleId); |
139 |
|
|
} |
140 |
|
|
|
141 |
|
5208 |
void Agent::StopTracing() { |
142 |
✓✓ |
5208 |
if (!started_) |
143 |
|
5152 |
return; |
144 |
|
|
// Perform final Flush on TraceBuffer. We don't want the tracing controller |
145 |
|
|
// to flush the buffer again on destruction of the V8::Platform. |
146 |
|
56 |
tracing_controller_->StopTracing(); |
147 |
|
56 |
tracing_controller_->Initialize(nullptr); |
148 |
|
56 |
started_ = false; |
149 |
|
|
|
150 |
|
|
// Thread should finish when the tracing loop is stopped. |
151 |
|
56 |
uv_thread_join(&thread_); |
152 |
|
|
} |
153 |
|
|
|
154 |
|
5263 |
void Agent::Disconnect(int client) { |
155 |
✓✓ |
5263 |
if (client == kDefaultHandleId) return; |
156 |
|
|
{ |
157 |
|
56 |
Mutex::ScopedLock lock(initialize_writer_mutex_); |
158 |
|
56 |
to_be_initialized_.erase(writers_[client].get()); |
159 |
|
|
} |
160 |
|
112 |
ScopedSuspendTracing suspend(tracing_controller_.get(), this); |
161 |
|
56 |
writers_.erase(client); |
162 |
|
56 |
categories_.erase(client); |
163 |
|
|
} |
164 |
|
|
|
165 |
|
20 |
void Agent::Enable(int id, const std::set<std::string>& categories) { |
166 |
✗✓ |
20 |
if (categories.empty()) |
167 |
|
|
return; |
168 |
|
|
|
169 |
|
|
ScopedSuspendTracing suspend(tracing_controller_.get(), this, |
170 |
|
40 |
id != kDefaultHandleId); |
171 |
|
20 |
categories_[id].insert(categories.begin(), categories.end()); |
172 |
|
|
} |
173 |
|
|
|
174 |
|
6 |
void Agent::Disable(int id, const std::set<std::string>& categories) { |
175 |
|
|
ScopedSuspendTracing suspend(tracing_controller_.get(), this, |
176 |
|
12 |
id != kDefaultHandleId); |
177 |
|
6 |
std::multiset<std::string>& writer_categories = categories_[id]; |
178 |
✓✓ |
12 |
for (const std::string& category : categories) { |
179 |
|
6 |
auto it = writer_categories.find(category); |
180 |
✓✗ |
6 |
if (it != writer_categories.end()) |
181 |
|
6 |
writer_categories.erase(it); |
182 |
|
|
} |
183 |
|
6 |
} |
184 |
|
|
|
185 |
|
138 |
TraceConfig* Agent::CreateTraceConfig() const { |
186 |
✓✓ |
138 |
if (categories_.empty()) |
187 |
|
2 |
return nullptr; |
188 |
|
136 |
TraceConfig* trace_config = new TraceConfig(); |
189 |
✓✓ |
281 |
for (const auto& category : flatten(categories_)) { |
190 |
|
145 |
trace_config->AddIncludedCategory(category.c_str()); |
191 |
|
|
} |
192 |
|
136 |
return trace_config; |
193 |
|
|
} |
194 |
|
|
|
195 |
|
17 |
std::string Agent::GetEnabledCategories() const { |
196 |
|
17 |
std::string categories; |
197 |
✓✓ |
33 |
for (const std::string& category : flatten(categories_)) { |
198 |
✓✓ |
16 |
if (!categories.empty()) |
199 |
|
2 |
categories += ','; |
200 |
|
16 |
categories += category; |
201 |
|
|
} |
202 |
|
17 |
return categories; |
203 |
|
|
} |
204 |
|
|
|
205 |
|
3127 |
void Agent::AppendTraceEvent(TraceObject* trace_event) { |
206 |
✓✓ |
5296 |
for (const auto& id_writer : writers_) |
207 |
|
2169 |
id_writer.second->AppendTraceEvent(trace_event); |
208 |
|
3127 |
} |
209 |
|
|
|
210 |
|
487 |
void Agent::AddMetadataEvent(std::unique_ptr<TraceObject> event) { |
211 |
|
974 |
Mutex::ScopedLock lock(metadata_events_mutex_); |
212 |
|
487 |
metadata_events_.push_back(std::move(event)); |
213 |
|
487 |
} |
214 |
|
|
|
215 |
|
272 |
void Agent::Flush(bool blocking) { |
216 |
|
|
{ |
217 |
|
544 |
Mutex::ScopedLock lock(metadata_events_mutex_); |
218 |
✓✓ |
2512 |
for (const auto& event : metadata_events_) |
219 |
|
2240 |
AppendTraceEvent(event.get()); |
220 |
|
|
} |
221 |
|
|
|
222 |
✓✓ |
436 |
for (const auto& id_writer : writers_) |
223 |
|
164 |
id_writer.second->Flush(blocking); |
224 |
|
272 |
} |
225 |
|
|
|
226 |
|
487 |
void TracingController::AddMetadataEvent( |
227 |
|
|
const unsigned char* category_group_enabled, |
228 |
|
|
const char* name, |
229 |
|
|
int num_args, |
230 |
|
|
const char** arg_names, |
231 |
|
|
const unsigned char* arg_types, |
232 |
|
|
const uint64_t* arg_values, |
233 |
|
|
std::unique_ptr<v8::ConvertableToTraceFormat>* convertable_values, |
234 |
|
|
unsigned int flags) { |
235 |
|
974 |
std::unique_ptr<TraceObject> trace_event(new TraceObject); |
236 |
|
974 |
trace_event->Initialize( |
237 |
|
|
TRACE_EVENT_PHASE_METADATA, category_group_enabled, name, |
238 |
|
|
node::tracing::kGlobalScope, // scope |
239 |
|
|
node::tracing::kNoId, // id |
240 |
|
|
node::tracing::kNoId, // bind_id |
241 |
|
|
num_args, arg_names, arg_types, arg_values, convertable_values, |
242 |
|
|
TRACE_EVENT_FLAG_NONE, |
243 |
|
487 |
CurrentTimestampMicroseconds(), |
244 |
|
487 |
CurrentCpuTimestampMicroseconds()); |
245 |
|
487 |
Agent* node_agent = node::tracing::TraceEventHelper::GetAgent(); |
246 |
✓✗ |
487 |
if (node_agent != nullptr) |
247 |
|
487 |
node_agent->AddMetadataEvent(std::move(trace_event)); |
248 |
|
487 |
} |
249 |
|
|
|
250 |
|
|
} // namespace tracing |
251 |
|
|
} // namespace node |