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:
ScopedSuspendTracing(TracingController * controller,Agent * agent,bool do_suspend=true)14 ScopedSuspendTracing(TracingController* controller, Agent* agent,
15 bool do_suspend = true)
16 : controller_(controller), agent_(do_suspend ? agent : nullptr) {
17 if (do_suspend) {
18 CHECK(agent_->started_);
19 controller->StopTracing();
20 }
21 }
22
~ScopedSuspendTracing()23 ~ScopedSuspendTracing() {
24 if (agent_ == nullptr) return;
25 TraceConfig* config = agent_->CreateTraceConfig();
26 if (config != nullptr) {
27 controller_->StartTracing(config);
28 }
29 }
30
31 private:
32 TracingController* controller_;
33 Agent* agent_;
34 };
35
36 namespace {
37
flatten(const std::unordered_map<int,std::multiset<std::string>> & map)38 std::set<std::string> flatten(
39 const std::unordered_map<int, std::multiset<std::string>>& map) {
40 std::set<std::string> result;
41 for (const auto& id_value : map)
42 result.insert(id_value.second.begin(), id_value.second.end());
43 return result;
44 }
45
46 } // namespace
47
48 using v8::platform::tracing::TraceConfig;
49 using v8::platform::tracing::TraceWriter;
50 using std::string;
51
Agent()52 Agent::Agent() : tracing_controller_(new TracingController()) {
53 tracing_controller_->Initialize(nullptr);
54
55 CHECK_EQ(uv_loop_init(&tracing_loop_), 0);
56 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 uv_unref(reinterpret_cast<uv_handle_t*>(&initialize_writer_async_));
63 }
64
InitializeWritersOnThread()65 void Agent::InitializeWritersOnThread() {
66 Mutex::ScopedLock lock(initialize_writer_mutex_);
67 while (!to_be_initialized_.empty()) {
68 AsyncTraceWriter* head = *to_be_initialized_.begin();
69 head->InitializeOnThread(&tracing_loop_);
70 to_be_initialized_.erase(head);
71 }
72 initialize_writer_condvar_.Broadcast(lock);
73 }
74
~Agent()75 Agent::~Agent() {
76 categories_.clear();
77 writers_.clear();
78
79 StopTracing();
80
81 uv_close(reinterpret_cast<uv_handle_t*>(&initialize_writer_async_), nullptr);
82 uv_run(&tracing_loop_, UV_RUN_ONCE);
83 CheckedUvLoopClose(&tracing_loop_);
84 }
85
Start()86 void Agent::Start() {
87 if (started_)
88 return;
89
90 NodeTraceBuffer* trace_buffer_ = new NodeTraceBuffer(
91 NodeTraceBuffer::kBufferChunks, this, &tracing_loop_);
92 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 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 started_ = true;
102 }
103
AddClient(const std::set<std::string> & categories,std::unique_ptr<AsyncTraceWriter> writer,enum UseDefaultCategoryMode mode)104 AgentWriterHandle Agent::AddClient(
105 const std::set<std::string>& categories,
106 std::unique_ptr<AsyncTraceWriter> writer,
107 enum UseDefaultCategoryMode mode) {
108 Start();
109
110 const std::set<std::string>* use_categories = &categories;
111
112 std::set<std::string> categories_with_default;
113 if (mode == kUseDefaultCategories) {
114 categories_with_default.insert(categories.begin(), categories.end());
115 categories_with_default.insert(categories_[kDefaultHandleId].begin(),
116 categories_[kDefaultHandleId].end());
117 use_categories = &categories_with_default;
118 }
119
120 ScopedSuspendTracing suspend(tracing_controller_.get(), this);
121 int id = next_writer_id_++;
122 AsyncTraceWriter* raw = writer.get();
123 writers_[id] = std::move(writer);
124 categories_[id] = { use_categories->begin(), use_categories->end() };
125
126 {
127 Mutex::ScopedLock lock(initialize_writer_mutex_);
128 to_be_initialized_.insert(raw);
129 uv_async_send(&initialize_writer_async_);
130 while (to_be_initialized_.count(raw) > 0)
131 initialize_writer_condvar_.Wait(lock);
132 }
133
134 return AgentWriterHandle(this, id);
135 }
136
DefaultHandle()137 AgentWriterHandle Agent::DefaultHandle() {
138 return AgentWriterHandle(this, kDefaultHandleId);
139 }
140
StopTracing()141 void Agent::StopTracing() {
142 if (!started_)
143 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 tracing_controller_->StopTracing();
147 tracing_controller_->Initialize(nullptr);
148 started_ = false;
149
150 // Thread should finish when the tracing loop is stopped.
151 uv_thread_join(&thread_);
152 }
153
Disconnect(int client)154 void Agent::Disconnect(int client) {
155 if (client == kDefaultHandleId) return;
156 {
157 Mutex::ScopedLock lock(initialize_writer_mutex_);
158 to_be_initialized_.erase(writers_[client].get());
159 }
160 ScopedSuspendTracing suspend(tracing_controller_.get(), this);
161 writers_.erase(client);
162 categories_.erase(client);
163 }
164
Enable(int id,const std::set<std::string> & categories)165 void Agent::Enable(int id, const std::set<std::string>& categories) {
166 if (categories.empty())
167 return;
168
169 ScopedSuspendTracing suspend(tracing_controller_.get(), this,
170 id != kDefaultHandleId);
171 categories_[id].insert(categories.begin(), categories.end());
172 }
173
Disable(int id,const std::set<std::string> & categories)174 void Agent::Disable(int id, const std::set<std::string>& categories) {
175 ScopedSuspendTracing suspend(tracing_controller_.get(), this,
176 id != kDefaultHandleId);
177 std::multiset<std::string>& writer_categories = categories_[id];
178 for (const std::string& category : categories) {
179 auto it = writer_categories.find(category);
180 if (it != writer_categories.end())
181 writer_categories.erase(it);
182 }
183 }
184
CreateTraceConfig() const185 TraceConfig* Agent::CreateTraceConfig() const {
186 if (categories_.empty())
187 return nullptr;
188 TraceConfig* trace_config = new TraceConfig();
189 for (const auto& category : flatten(categories_)) {
190 trace_config->AddIncludedCategory(category.c_str());
191 }
192 return trace_config;
193 }
194
GetEnabledCategories() const195 std::string Agent::GetEnabledCategories() const {
196 std::string categories;
197 for (const std::string& category : flatten(categories_)) {
198 if (!categories.empty())
199 categories += ',';
200 categories += category;
201 }
202 return categories;
203 }
204
AppendTraceEvent(TraceObject * trace_event)205 void Agent::AppendTraceEvent(TraceObject* trace_event) {
206 for (const auto& id_writer : writers_)
207 id_writer.second->AppendTraceEvent(trace_event);
208 }
209
AddMetadataEvent(std::unique_ptr<TraceObject> event)210 void Agent::AddMetadataEvent(std::unique_ptr<TraceObject> event) {
211 Mutex::ScopedLock lock(metadata_events_mutex_);
212 metadata_events_.push_back(std::move(event));
213 }
214
Flush(bool blocking)215 void Agent::Flush(bool blocking) {
216 {
217 Mutex::ScopedLock lock(metadata_events_mutex_);
218 for (const auto& event : metadata_events_)
219 AppendTraceEvent(event.get());
220 }
221
222 for (const auto& id_writer : writers_)
223 id_writer.second->Flush(blocking);
224 }
225
AddMetadataEvent(const unsigned char * category_group_enabled,const char * name,int num_args,const char ** arg_names,const unsigned char * arg_types,const uint64_t * arg_values,std::unique_ptr<v8::ConvertableToTraceFormat> * convertable_values,unsigned int flags)226 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 std::unique_ptr<TraceObject> trace_event(new TraceObject);
236 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 CurrentTimestampMicroseconds(),
244 CurrentCpuTimestampMicroseconds());
245 Agent* node_agent = node::tracing::TraceEventHelper::GetAgent();
246 if (node_agent != nullptr)
247 node_agent->AddMetadataEvent(std::move(trace_event));
248 }
249
250 } // namespace tracing
251 } // namespace node
252