• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include "include/libplatform/v8-tracing.h"
10 #include "src/base/atomicops.h"
11 #include "src/base/platform/mutex.h"
12 #include "src/base/platform/time.h"
13 #include "src/base/platform/wrappers.h"
14 
15 #ifdef V8_USE_PERFETTO
16 #include "perfetto/ext/trace_processor/export_json.h"
17 #include "perfetto/trace_processor/trace_processor.h"
18 #include "perfetto/tracing/tracing.h"
19 #include "protos/perfetto/config/data_source_config.gen.h"
20 #include "protos/perfetto/config/trace_config.gen.h"
21 #include "protos/perfetto/config/track_event/track_event_config.gen.h"
22 #include "src/base/platform/platform.h"
23 #include "src/base/platform/semaphore.h"
24 #include "src/libplatform/tracing/trace-event-listener.h"
25 #endif  // V8_USE_PERFETTO
26 
27 #ifdef V8_USE_PERFETTO
28 class JsonOutputWriter : public perfetto::trace_processor::json::OutputWriter {
29  public:
JsonOutputWriter(std::ostream * stream)30   explicit JsonOutputWriter(std::ostream* stream) : stream_(stream) {}
31 
AppendString(const std::string & string)32   perfetto::trace_processor::util::Status AppendString(
33       const std::string& string) override {
34     *stream_ << string;
35     return perfetto::trace_processor::util::OkStatus();
36   }
37 
38  private:
39   std::ostream* stream_;
40 };
41 #endif  // V8_USE_PERFETTO
42 
43 namespace v8 {
44 namespace platform {
45 namespace tracing {
46 
47 #if !defined(V8_USE_PERFETTO)
48 static const size_t kMaxCategoryGroups = 200;
49 
50 // Parallel arrays g_category_groups and g_category_group_enabled are separate
51 // so that a pointer to a member of g_category_group_enabled can be easily
52 // converted to an index into g_category_groups. This allows macros to deal
53 // only with char enabled pointers from g_category_group_enabled, and we can
54 // convert internally to determine the category name from the char enabled
55 // pointer.
56 const char* g_category_groups[kMaxCategoryGroups] = {
57     "toplevel",
58     "tracing categories exhausted; must increase kMaxCategoryGroups",
59     "__metadata"};
60 
61 // The enabled flag is char instead of bool so that the API can be used from C.
62 unsigned char g_category_group_enabled[kMaxCategoryGroups] = {0};
63 // Indexes here have to match the g_category_groups array indexes above.
64 const int g_category_categories_exhausted = 1;
65 // Metadata category not used in V8.
66 // const int g_category_metadata = 2;
67 const int g_num_builtin_categories = 3;
68 
69 // Skip default categories.
70 v8::base::AtomicWord g_category_index = g_num_builtin_categories;
71 #endif  // !defined(V8_USE_PERFETTO)
72 
TracingController()73 TracingController::TracingController() { mutex_.reset(new base::Mutex()); }
74 
~TracingController()75 TracingController::~TracingController() {
76   StopTracing();
77 
78 #if !defined(V8_USE_PERFETTO)
79   {
80     // Free memory for category group names allocated via strdup.
81     base::MutexGuard lock(mutex_.get());
82     for (size_t i = g_category_index - 1; i >= g_num_builtin_categories; --i) {
83       const char* group = g_category_groups[i];
84       g_category_groups[i] = nullptr;
85       free(const_cast<char*>(group));
86     }
87     g_category_index = g_num_builtin_categories;
88   }
89 #endif  // !defined(V8_USE_PERFETTO)
90 }
91 
92 #ifdef V8_USE_PERFETTO
InitializeForPerfetto(std::ostream * output_stream)93 void TracingController::InitializeForPerfetto(std::ostream* output_stream) {
94   output_stream_ = output_stream;
95   DCHECK_NOT_NULL(output_stream);
96   DCHECK(output_stream->good());
97 }
98 
SetTraceEventListenerForTesting(TraceEventListener * listener)99 void TracingController::SetTraceEventListenerForTesting(
100     TraceEventListener* listener) {
101   listener_for_testing_ = listener;
102 }
103 #else   // !V8_USE_PERFETTO
Initialize(TraceBuffer * trace_buffer)104 void TracingController::Initialize(TraceBuffer* trace_buffer) {
105   trace_buffer_.reset(trace_buffer);
106 }
107 
CurrentTimestampMicroseconds()108 int64_t TracingController::CurrentTimestampMicroseconds() {
109   return base::TimeTicks::Now().ToInternalValue();
110 }
111 
CurrentCpuTimestampMicroseconds()112 int64_t TracingController::CurrentCpuTimestampMicroseconds() {
113   return base::ThreadTicks::Now().ToInternalValue();
114 }
115 
AddTraceEvent(char phase,const uint8_t * category_enabled_flag,const char * name,const char * scope,uint64_t id,uint64_t bind_id,int num_args,const char ** arg_names,const uint8_t * arg_types,const uint64_t * arg_values,std::unique_ptr<v8::ConvertableToTraceFormat> * arg_convertables,unsigned int flags)116 uint64_t TracingController::AddTraceEvent(
117     char phase, const uint8_t* category_enabled_flag, const char* name,
118     const char* scope, uint64_t id, uint64_t bind_id, int num_args,
119     const char** arg_names, const uint8_t* arg_types,
120     const uint64_t* arg_values,
121     std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
122     unsigned int flags) {
123   int64_t now_us = CurrentTimestampMicroseconds();
124 
125   return AddTraceEventWithTimestamp(
126       phase, category_enabled_flag, name, scope, id, bind_id, num_args,
127       arg_names, arg_types, arg_values, arg_convertables, flags, now_us);
128 }
129 
AddTraceEventWithTimestamp(char phase,const uint8_t * category_enabled_flag,const char * name,const char * scope,uint64_t id,uint64_t bind_id,int num_args,const char ** arg_names,const uint8_t * arg_types,const uint64_t * arg_values,std::unique_ptr<v8::ConvertableToTraceFormat> * arg_convertables,unsigned int flags,int64_t timestamp)130 uint64_t TracingController::AddTraceEventWithTimestamp(
131     char phase, const uint8_t* category_enabled_flag, const char* name,
132     const char* scope, uint64_t id, uint64_t bind_id, int num_args,
133     const char** arg_names, const uint8_t* arg_types,
134     const uint64_t* arg_values,
135     std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
136     unsigned int flags, int64_t timestamp) {
137   int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();
138 
139   uint64_t handle = 0;
140   if (recording_.load(std::memory_order_acquire)) {
141     TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle);
142     if (trace_object) {
143       {
144         base::MutexGuard lock(mutex_.get());
145         trace_object->Initialize(phase, category_enabled_flag, name, scope, id,
146                                  bind_id, num_args, arg_names, arg_types,
147                                  arg_values, arg_convertables, flags, timestamp,
148                                  cpu_now_us);
149       }
150     }
151   }
152   return handle;
153 }
154 
UpdateTraceEventDuration(const uint8_t * category_enabled_flag,const char * name,uint64_t handle)155 void TracingController::UpdateTraceEventDuration(
156     const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {
157   int64_t now_us = CurrentTimestampMicroseconds();
158   int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();
159 
160   TraceObject* trace_object = trace_buffer_->GetEventByHandle(handle);
161   if (!trace_object) return;
162   trace_object->UpdateDuration(now_us, cpu_now_us);
163 }
164 
GetCategoryGroupName(const uint8_t * category_group_enabled)165 const char* TracingController::GetCategoryGroupName(
166     const uint8_t* category_group_enabled) {
167   // Calculate the index of the category group by finding
168   // category_group_enabled in g_category_group_enabled array.
169   uintptr_t category_begin =
170       reinterpret_cast<uintptr_t>(g_category_group_enabled);
171   uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_group_enabled);
172   // Check for out of bounds category pointers.
173   DCHECK(category_ptr >= category_begin &&
174          category_ptr < reinterpret_cast<uintptr_t>(g_category_group_enabled +
175                                                     kMaxCategoryGroups));
176   uintptr_t category_index =
177       (category_ptr - category_begin) / sizeof(g_category_group_enabled[0]);
178   return g_category_groups[category_index];
179 }
180 #endif  // !defined(V8_USE_PERFETTO)
181 
StartTracing(TraceConfig * trace_config)182 void TracingController::StartTracing(TraceConfig* trace_config) {
183 #ifdef V8_USE_PERFETTO
184   DCHECK_NOT_NULL(output_stream_);
185   DCHECK(output_stream_->good());
186   perfetto::trace_processor::Config processor_config;
187   trace_processor_ =
188       perfetto::trace_processor::TraceProcessorStorage::CreateInstance(
189           processor_config);
190 
191   ::perfetto::TraceConfig perfetto_trace_config;
192   perfetto_trace_config.add_buffers()->set_size_kb(4096);
193   auto ds_config = perfetto_trace_config.add_data_sources()->mutable_config();
194   ds_config->set_name("track_event");
195   perfetto::protos::gen::TrackEventConfig te_config;
196   te_config.add_disabled_categories("*");
197   for (const auto& category : trace_config->GetEnabledCategories())
198     te_config.add_enabled_categories(category);
199   ds_config->set_track_event_config_raw(te_config.SerializeAsString());
200 
201   tracing_session_ =
202       perfetto::Tracing::NewTrace(perfetto::BackendType::kUnspecifiedBackend);
203   tracing_session_->Setup(perfetto_trace_config);
204   tracing_session_->StartBlocking();
205 
206 #endif  // V8_USE_PERFETTO
207 
208   trace_config_.reset(trace_config);
209   std::unordered_set<v8::TracingController::TraceStateObserver*> observers_copy;
210   {
211     base::MutexGuard lock(mutex_.get());
212     recording_.store(true, std::memory_order_release);
213 #ifndef V8_USE_PERFETTO
214     UpdateCategoryGroupEnabledFlags();
215 #endif
216     observers_copy = observers_;
217   }
218   for (auto o : observers_copy) {
219     o->OnTraceEnabled();
220   }
221 }
222 
StopTracing()223 void TracingController::StopTracing() {
224   bool expected = true;
225   if (!recording_.compare_exchange_strong(expected, false)) {
226     return;
227   }
228 #ifndef V8_USE_PERFETTO
229   UpdateCategoryGroupEnabledFlags();
230 #endif
231   std::unordered_set<v8::TracingController::TraceStateObserver*> observers_copy;
232   {
233     base::MutexGuard lock(mutex_.get());
234     observers_copy = observers_;
235   }
236   for (auto o : observers_copy) {
237     o->OnTraceDisabled();
238   }
239 
240 #ifdef V8_USE_PERFETTO
241   tracing_session_->StopBlocking();
242 
243   std::vector<char> trace = tracing_session_->ReadTraceBlocking();
244   std::unique_ptr<uint8_t[]> trace_bytes(new uint8_t[trace.size()]);
245   std::copy(&trace[0], &trace[0] + trace.size(), &trace_bytes[0]);
246   trace_processor_->Parse(std::move(trace_bytes), trace.size());
247   trace_processor_->NotifyEndOfFile();
248   JsonOutputWriter output_writer(output_stream_);
249   auto status = perfetto::trace_processor::json::ExportJson(
250       trace_processor_.get(), &output_writer, nullptr, nullptr, nullptr);
251   DCHECK(status.ok());
252 
253   if (listener_for_testing_) listener_for_testing_->ParseFromArray(trace);
254 
255   trace_processor_.reset();
256 #else
257 
258   {
259     base::MutexGuard lock(mutex_.get());
260     DCHECK(trace_buffer_);
261     trace_buffer_->Flush();
262   }
263 #endif  // V8_USE_PERFETTO
264 }
265 
266 #if !defined(V8_USE_PERFETTO)
UpdateCategoryGroupEnabledFlag(size_t category_index)267 void TracingController::UpdateCategoryGroupEnabledFlag(size_t category_index) {
268   unsigned char enabled_flag = 0;
269   const char* category_group = g_category_groups[category_index];
270   if (recording_.load(std::memory_order_acquire) &&
271       trace_config_->IsCategoryGroupEnabled(category_group)) {
272     enabled_flag |= ENABLED_FOR_RECORDING;
273   }
274 
275   // TODO(fmeawad): EventCallback and ETW modes are not yet supported in V8.
276   // TODO(primiano): this is a temporary workaround for catapult:#2341,
277   // to guarantee that metadata events are always added even if the category
278   // filter is "-*". See crbug.com/618054 for more details and long-term fix.
279   if (recording_.load(std::memory_order_acquire) &&
280       !strcmp(category_group, "__metadata")) {
281     enabled_flag |= ENABLED_FOR_RECORDING;
282   }
283 
284   base::Relaxed_Store(reinterpret_cast<base::Atomic8*>(
285                           g_category_group_enabled + category_index),
286                       enabled_flag);
287 }
288 
UpdateCategoryGroupEnabledFlags()289 void TracingController::UpdateCategoryGroupEnabledFlags() {
290   size_t category_index = base::Acquire_Load(&g_category_index);
291   for (size_t i = 0; i < category_index; i++) UpdateCategoryGroupEnabledFlag(i);
292 }
293 
GetCategoryGroupEnabled(const char * category_group)294 const uint8_t* TracingController::GetCategoryGroupEnabled(
295     const char* category_group) {
296   // Check that category group does not contain double quote
297   DCHECK(!strchr(category_group, '"'));
298 
299   // The g_category_groups is append only, avoid using a lock for the fast path.
300   size_t category_index = base::Acquire_Load(&g_category_index);
301 
302   // Search for pre-existing category group.
303   for (size_t i = 0; i < category_index; ++i) {
304     if (strcmp(g_category_groups[i], category_group) == 0) {
305       return &g_category_group_enabled[i];
306     }
307   }
308 
309   // Slow path. Grab the lock.
310   base::MutexGuard lock(mutex_.get());
311 
312   // Check the list again with lock in hand.
313   unsigned char* category_group_enabled = nullptr;
314   category_index = base::Acquire_Load(&g_category_index);
315   for (size_t i = 0; i < category_index; ++i) {
316     if (strcmp(g_category_groups[i], category_group) == 0) {
317       return &g_category_group_enabled[i];
318     }
319   }
320 
321   // Create a new category group.
322   // Check that there is a slot for the new category_group.
323   DCHECK(category_index < kMaxCategoryGroups);
324   if (category_index < kMaxCategoryGroups) {
325     // Don't hold on to the category_group pointer, so that we can create
326     // category groups with strings not known at compile time (this is
327     // required by SetWatchEvent).
328     const char* new_group = base::Strdup(category_group);
329     g_category_groups[category_index] = new_group;
330     DCHECK(!g_category_group_enabled[category_index]);
331     // Note that if both included and excluded patterns in the
332     // TraceConfig are empty, we exclude nothing,
333     // thereby enabling this category group.
334     UpdateCategoryGroupEnabledFlag(category_index);
335     category_group_enabled = &g_category_group_enabled[category_index];
336     // Update the max index now.
337     base::Release_Store(&g_category_index, category_index + 1);
338   } else {
339     category_group_enabled =
340         &g_category_group_enabled[g_category_categories_exhausted];
341   }
342   return category_group_enabled;
343 }
344 #endif  // !defined(V8_USE_PERFETTO)
345 
AddTraceStateObserver(v8::TracingController::TraceStateObserver * observer)346 void TracingController::AddTraceStateObserver(
347     v8::TracingController::TraceStateObserver* observer) {
348   {
349     base::MutexGuard lock(mutex_.get());
350     observers_.insert(observer);
351     if (!recording_.load(std::memory_order_acquire)) return;
352   }
353   // Fire the observer if recording is already in progress.
354   observer->OnTraceEnabled();
355 }
356 
RemoveTraceStateObserver(v8::TracingController::TraceStateObserver * observer)357 void TracingController::RemoveTraceStateObserver(
358     v8::TracingController::TraceStateObserver* observer) {
359   base::MutexGuard lock(mutex_.get());
360   DCHECK(observers_.find(observer) != observers_.end());
361   observers_.erase(observer);
362 }
363 
364 }  // namespace tracing
365 }  // namespace platform
366 }  // namespace v8
367