• 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 
11 #include "src/base/atomicops.h"
12 #include "src/base/platform/mutex.h"
13 #include "src/base/platform/time.h"
14 
15 namespace v8 {
16 namespace platform {
17 namespace tracing {
18 
19 #define MAX_CATEGORY_GROUPS 200
20 
21 // Parallel arrays g_category_groups and g_category_group_enabled are separate
22 // so that a pointer to a member of g_category_group_enabled can be easily
23 // converted to an index into g_category_groups. This allows macros to deal
24 // only with char enabled pointers from g_category_group_enabled, and we can
25 // convert internally to determine the category name from the char enabled
26 // pointer.
27 const char* g_category_groups[MAX_CATEGORY_GROUPS] = {
28     "toplevel",
29     "tracing categories exhausted; must increase MAX_CATEGORY_GROUPS",
30     "__metadata"};
31 
32 // The enabled flag is char instead of bool so that the API can be used from C.
33 unsigned char g_category_group_enabled[MAX_CATEGORY_GROUPS] = {0};
34 // Indexes here have to match the g_category_groups array indexes above.
35 const int g_category_categories_exhausted = 1;
36 // Metadata category not used in V8.
37 // const int g_category_metadata = 2;
38 const int g_num_builtin_categories = 3;
39 
40 // Skip default categories.
41 v8::base::AtomicWord g_category_index = g_num_builtin_categories;
42 
TracingController()43 TracingController::TracingController() {}
44 
~TracingController()45 TracingController::~TracingController() {
46   StopTracing();
47 
48   {
49     // Free memory for category group names allocated via strdup.
50     base::LockGuard<base::Mutex> lock(mutex_.get());
51     for (size_t i = g_category_index - 1; i >= g_num_builtin_categories; --i) {
52       const char* group = g_category_groups[i];
53       g_category_groups[i] = nullptr;
54       free(const_cast<char*>(group));
55     }
56     g_category_index = g_num_builtin_categories;
57   }
58 }
59 
Initialize(TraceBuffer * trace_buffer)60 void TracingController::Initialize(TraceBuffer* trace_buffer) {
61   trace_buffer_.reset(trace_buffer);
62   mutex_.reset(new base::Mutex());
63 }
64 
CurrentTimestampMicroseconds()65 int64_t TracingController::CurrentTimestampMicroseconds() {
66   return base::TimeTicks::HighResolutionNow().ToInternalValue();
67 }
68 
CurrentCpuTimestampMicroseconds()69 int64_t TracingController::CurrentCpuTimestampMicroseconds() {
70   return base::ThreadTicks::Now().ToInternalValue();
71 }
72 
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)73 uint64_t TracingController::AddTraceEvent(
74     char phase, const uint8_t* category_enabled_flag, const char* name,
75     const char* scope, uint64_t id, uint64_t bind_id, int num_args,
76     const char** arg_names, const uint8_t* arg_types,
77     const uint64_t* arg_values,
78     std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
79     unsigned int flags) {
80   uint64_t handle;
81   TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle);
82   if (trace_object) {
83     trace_object->Initialize(
84         phase, category_enabled_flag, name, scope, id, bind_id, num_args,
85         arg_names, arg_types, arg_values, arg_convertables, flags,
86         CurrentTimestampMicroseconds(), CurrentCpuTimestampMicroseconds());
87   }
88   return handle;
89 }
90 
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)91 uint64_t TracingController::AddTraceEventWithTimestamp(
92     char phase, const uint8_t* category_enabled_flag, const char* name,
93     const char* scope, uint64_t id, uint64_t bind_id, int num_args,
94     const char** arg_names, const uint8_t* arg_types,
95     const uint64_t* arg_values,
96     std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
97     unsigned int flags, int64_t timestamp) {
98   uint64_t handle;
99   TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle);
100   if (trace_object) {
101     trace_object->Initialize(phase, category_enabled_flag, name, scope, id,
102                              bind_id, num_args, arg_names, arg_types,
103                              arg_values, arg_convertables, flags, timestamp,
104                              CurrentCpuTimestampMicroseconds());
105   }
106   return handle;
107 }
108 
UpdateTraceEventDuration(const uint8_t * category_enabled_flag,const char * name,uint64_t handle)109 void TracingController::UpdateTraceEventDuration(
110     const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {
111   TraceObject* trace_object = trace_buffer_->GetEventByHandle(handle);
112   if (!trace_object) return;
113   trace_object->UpdateDuration(CurrentTimestampMicroseconds(),
114                                CurrentCpuTimestampMicroseconds());
115 }
116 
GetCategoryGroupEnabled(const char * category_group)117 const uint8_t* TracingController::GetCategoryGroupEnabled(
118     const char* category_group) {
119   return GetCategoryGroupEnabledInternal(category_group);
120 }
121 
GetCategoryGroupName(const uint8_t * category_group_enabled)122 const char* TracingController::GetCategoryGroupName(
123     const uint8_t* category_group_enabled) {
124   // Calculate the index of the category group by finding
125   // category_group_enabled in g_category_group_enabled array.
126   uintptr_t category_begin =
127       reinterpret_cast<uintptr_t>(g_category_group_enabled);
128   uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_group_enabled);
129   // Check for out of bounds category pointers.
130   DCHECK(category_ptr >= category_begin &&
131          category_ptr < reinterpret_cast<uintptr_t>(g_category_group_enabled +
132                                                     MAX_CATEGORY_GROUPS));
133   uintptr_t category_index =
134       (category_ptr - category_begin) / sizeof(g_category_group_enabled[0]);
135   return g_category_groups[category_index];
136 }
137 
StartTracing(TraceConfig * trace_config)138 void TracingController::StartTracing(TraceConfig* trace_config) {
139   trace_config_.reset(trace_config);
140   std::unordered_set<v8::TracingController::TraceStateObserver*> observers_copy;
141   {
142     base::LockGuard<base::Mutex> lock(mutex_.get());
143     mode_ = RECORDING_MODE;
144     UpdateCategoryGroupEnabledFlags();
145     observers_copy = observers_;
146   }
147   for (auto o : observers_copy) {
148     o->OnTraceEnabled();
149   }
150 }
151 
StopTracing()152 void TracingController::StopTracing() {
153   if (mode_ == DISABLED) {
154     return;
155   }
156   DCHECK(trace_buffer_);
157   mode_ = DISABLED;
158   UpdateCategoryGroupEnabledFlags();
159   std::unordered_set<v8::TracingController::TraceStateObserver*> observers_copy;
160   {
161     base::LockGuard<base::Mutex> lock(mutex_.get());
162     observers_copy = observers_;
163   }
164   for (auto o : observers_copy) {
165     o->OnTraceDisabled();
166   }
167   trace_buffer_->Flush();
168 }
169 
UpdateCategoryGroupEnabledFlag(size_t category_index)170 void TracingController::UpdateCategoryGroupEnabledFlag(size_t category_index) {
171   unsigned char enabled_flag = 0;
172   const char* category_group = g_category_groups[category_index];
173   if (mode_ == RECORDING_MODE &&
174       trace_config_->IsCategoryGroupEnabled(category_group)) {
175     enabled_flag |= ENABLED_FOR_RECORDING;
176   }
177 
178   // TODO(fmeawad): EventCallback and ETW modes are not yet supported in V8.
179   // TODO(primiano): this is a temporary workaround for catapult:#2341,
180   // to guarantee that metadata events are always added even if the category
181   // filter is "-*". See crbug.com/618054 for more details and long-term fix.
182   if (mode_ == RECORDING_MODE && !strcmp(category_group, "__metadata")) {
183     enabled_flag |= ENABLED_FOR_RECORDING;
184   }
185 
186   base::Relaxed_Store(reinterpret_cast<base::Atomic8*>(
187                           g_category_group_enabled + category_index),
188                       enabled_flag);
189 }
190 
UpdateCategoryGroupEnabledFlags()191 void TracingController::UpdateCategoryGroupEnabledFlags() {
192   size_t category_index = base::Relaxed_Load(&g_category_index);
193   for (size_t i = 0; i < category_index; i++) UpdateCategoryGroupEnabledFlag(i);
194 }
195 
GetCategoryGroupEnabledInternal(const char * category_group)196 const uint8_t* TracingController::GetCategoryGroupEnabledInternal(
197     const char* category_group) {
198   // Check that category groups does not contain double quote
199   DCHECK(!strchr(category_group, '"'));
200 
201   // The g_category_groups is append only, avoid using a lock for the fast path.
202   size_t category_index = base::Acquire_Load(&g_category_index);
203 
204   // Search for pre-existing category group.
205   for (size_t i = 0; i < category_index; ++i) {
206     if (strcmp(g_category_groups[i], category_group) == 0) {
207       return &g_category_group_enabled[i];
208     }
209   }
210 
211   // Slow path. Grab the lock.
212   base::LockGuard<base::Mutex> lock(mutex_.get());
213 
214   // Check the list again with lock in hand.
215   unsigned char* category_group_enabled = nullptr;
216   category_index = base::Acquire_Load(&g_category_index);
217   for (size_t i = 0; i < category_index; ++i) {
218     if (strcmp(g_category_groups[i], category_group) == 0) {
219       return &g_category_group_enabled[i];
220     }
221   }
222 
223   // Create a new category group.
224   // Check that there is a slot for the new category_group.
225   DCHECK(category_index < MAX_CATEGORY_GROUPS);
226   if (category_index < MAX_CATEGORY_GROUPS) {
227     // Don't hold on to the category_group pointer, so that we can create
228     // category groups with strings not known at compile time (this is
229     // required by SetWatchEvent).
230     const char* new_group = strdup(category_group);
231     g_category_groups[category_index] = new_group;
232     DCHECK(!g_category_group_enabled[category_index]);
233     // Note that if both included and excluded patterns in the
234     // TraceConfig are empty, we exclude nothing,
235     // thereby enabling this category group.
236     UpdateCategoryGroupEnabledFlag(category_index);
237     category_group_enabled = &g_category_group_enabled[category_index];
238     // Update the max index now.
239     base::Release_Store(&g_category_index, category_index + 1);
240   } else {
241     category_group_enabled =
242         &g_category_group_enabled[g_category_categories_exhausted];
243   }
244   return category_group_enabled;
245 }
246 
AddTraceStateObserver(v8::TracingController::TraceStateObserver * observer)247 void TracingController::AddTraceStateObserver(
248     v8::TracingController::TraceStateObserver* observer) {
249   {
250     base::LockGuard<base::Mutex> lock(mutex_.get());
251     observers_.insert(observer);
252     if (mode_ != RECORDING_MODE) return;
253   }
254   // Fire the observer if recording is already in progress.
255   observer->OnTraceEnabled();
256 }
257 
RemoveTraceStateObserver(v8::TracingController::TraceStateObserver * observer)258 void TracingController::RemoveTraceStateObserver(
259     v8::TracingController::TraceStateObserver* observer) {
260   base::LockGuard<base::Mutex> lock(mutex_.get());
261   DCHECK(observers_.find(observer) != observers_.end());
262   observers_.erase(observer);
263 }
264 
265 }  // namespace tracing
266 }  // namespace platform
267 }  // namespace v8
268