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