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