• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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