v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
tracing-controller.cc
Go to the documentation of this file.
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
10#include "src/base/atomicops.h"
13
14#ifdef V8_USE_PERFETTO
15#include "perfetto/ext/trace_processor/export_json.h"
16#include "perfetto/trace_processor/trace_processor.h"
17#include "perfetto/tracing/tracing.h"
18#include "protos/perfetto/config/data_source_config.gen.h"
19#include "protos/perfetto/config/trace_config.gen.h"
20#include "protos/perfetto/config/track_event/track_event_config.gen.h"
24#endif // V8_USE_PERFETTO
25
26#ifdef V8_USE_PERFETTO
27class JsonOutputWriter : public perfetto::trace_processor::json::OutputWriter {
28 public:
29 explicit JsonOutputWriter(std::ostream* stream) : stream_(stream) {}
30
31 perfetto::trace_processor::util::Status AppendString(
32 const std::string& string) override {
33 *stream_ << string;
34 return perfetto::trace_processor::util::OkStatus();
35 }
36
37 private:
38 std::ostream* stream_;
39};
40#endif // V8_USE_PERFETTO
41
42namespace v8 {
43namespace platform {
44namespace tracing {
45
46#if !defined(V8_USE_PERFETTO)
47static const size_t kMaxCategoryGroups = 200;
48
49// Parallel arrays g_category_groups and g_category_group_enabled are separate
50// so that a pointer to a member of g_category_group_enabled can be easily
51// converted to an index into g_category_groups. This allows macros to deal
52// only with char enabled pointers from g_category_group_enabled, and we can
53// convert internally to determine the category name from the char enabled
54// pointer.
56 "toplevel",
57 "tracing categories exhausted; must increase kMaxCategoryGroups",
58 "__metadata"};
59
60// The enabled flag is char instead of bool so that the API can be used from C.
62// Indexes here have to match the g_category_groups array indexes above.
64// Metadata category not used in V8.
65// const int g_category_metadata = 2;
67
68// Skip default categories.
70#endif // !defined(V8_USE_PERFETTO)
71
73
76
77#if !defined(V8_USE_PERFETTO)
78 {
79 // Free memory for category group names allocated via strdup.
80 base::MutexGuard lock(mutex_.get());
81 for (size_t i = g_category_index - 1; i >= g_num_builtin_categories; --i) {
82 const char* group = g_category_groups[i];
83 g_category_groups[i] = nullptr;
84 free(const_cast<char*>(group));
85 }
87 }
88#endif // !defined(V8_USE_PERFETTO)
89}
90
91#ifdef V8_USE_PERFETTO
92void TracingController::InitializeForPerfetto(std::ostream* output_stream) {
93 output_stream_ = output_stream;
94 DCHECK_NOT_NULL(output_stream);
95 DCHECK(output_stream->good());
96}
97
98void TracingController::SetTraceEventListenerForTesting(
99 TraceEventListener* listener) {
100 listener_for_testing_ = listener;
101}
102#else // !V8_USE_PERFETTO
104 trace_buffer_.reset(trace_buffer);
105}
106
110
114
116 char phase, const uint8_t* category_enabled_flag, const char* name,
117 const char* scope, uint64_t id, uint64_t bind_id, int num_args,
118 const char** arg_names, const uint8_t* arg_types,
119 const uint64_t* arg_values,
120 std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
121 unsigned int flags) {
122 int64_t now_us = CurrentTimestampMicroseconds();
123
125 phase, category_enabled_flag, name, scope, id, bind_id, num_args,
126 arg_names, arg_types, arg_values, arg_convertables, flags, now_us);
127}
128
130 char phase, const uint8_t* category_enabled_flag, const char* name,
131 const char* scope, uint64_t id, uint64_t bind_id, int num_args,
132 const char** arg_names, const uint8_t* arg_types,
133 const uint64_t* arg_values,
134 std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
135 unsigned int flags, int64_t timestamp) {
136 int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();
137
138 uint64_t handle = 0;
139 if (recording_.load(std::memory_order_acquire)) {
140 TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle);
141 if (trace_object) {
142 {
143 base::MutexGuard lock(mutex_.get());
144 trace_object->Initialize(phase, category_enabled_flag, name, scope, id,
145 bind_id, num_args, arg_names, arg_types,
146 arg_values, arg_convertables, flags, timestamp,
147 cpu_now_us);
148 }
149 }
150 }
151 return handle;
152}
153
155 const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {
156 int64_t now_us = CurrentTimestampMicroseconds();
157 int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();
158
159 TraceObject* trace_object = trace_buffer_->GetEventByHandle(handle);
160 if (!trace_object) return;
161 trace_object->UpdateDuration(now_us, cpu_now_us);
162}
163
165 const uint8_t* category_group_enabled) {
166 // Calculate the index of the category group by finding
167 // category_group_enabled in g_category_group_enabled array.
168 uintptr_t category_begin =
169 reinterpret_cast<uintptr_t>(g_category_group_enabled);
170 uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_group_enabled);
171 // Check for out of bounds category pointers.
172 DCHECK(category_ptr >= category_begin &&
173 category_ptr < reinterpret_cast<uintptr_t>(g_category_group_enabled +
175 uintptr_t category_index =
176 (category_ptr - category_begin) / sizeof(g_category_group_enabled[0]);
177 return g_category_groups[category_index];
178}
179#endif // !defined(V8_USE_PERFETTO)
180
182#ifdef V8_USE_PERFETTO
183 DCHECK_NOT_NULL(output_stream_);
184 DCHECK(output_stream_->good());
185 perfetto::trace_processor::Config processor_config;
186 trace_processor_ =
187 perfetto::trace_processor::TraceProcessorStorage::CreateInstance(
188 processor_config);
189
190 ::perfetto::TraceConfig perfetto_trace_config;
191 perfetto_trace_config.add_buffers()->set_size_kb(4096);
192 auto ds_config = perfetto_trace_config.add_data_sources()->mutable_config();
193 ds_config->set_name("track_event");
194 perfetto::protos::gen::TrackEventConfig te_config;
195 te_config.add_disabled_categories("*");
196 for (const auto& category : trace_config->GetEnabledCategories())
197 te_config.add_enabled_categories(category);
198 ds_config->set_track_event_config_raw(te_config.SerializeAsString());
199
200 tracing_session_ =
201 perfetto::Tracing::NewTrace(perfetto::BackendType::kUnspecifiedBackend);
202 tracing_session_->Setup(perfetto_trace_config);
203 tracing_session_->StartBlocking();
204
205#endif // V8_USE_PERFETTO
206
207 trace_config_.reset(trace_config);
208 recording_.store(true, std::memory_order_release);
209
210#ifndef V8_USE_PERFETTO
211 std::unordered_set<v8::TracingController::TraceStateObserver*> observers_copy;
212 {
213 base::MutexGuard lock(mutex_.get());
215 observers_copy = observers_;
216 }
217 for (auto o : observers_copy) {
218 o->OnTraceEnabled();
219 }
220#endif
221}
222
224 bool expected = true;
225 if (!recording_.compare_exchange_strong(expected, false)) {
226 return;
227 }
228#ifndef V8_USE_PERFETTO
230 std::unordered_set<v8::TracingController::TraceStateObserver*> observers_copy;
231 {
232 base::MutexGuard lock(mutex_.get());
233 observers_copy = observers_;
234 }
235 for (auto o : observers_copy) {
236 o->OnTraceDisabled();
237 }
238#endif
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());
261 trace_buffer_->Flush();
262 }
263#endif // V8_USE_PERFETTO
264}
265
266#if !defined(V8_USE_PERFETTO)
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
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
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) {
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) {
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 = 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 =
341 }
342 return category_group_enabled;
343}
344
347 {
348 base::MutexGuard lock(mutex_.get());
349 observers_.insert(observer);
350 if (!recording_.load(std::memory_order_acquire)) return;
351 }
352 // Fire the observer if recording is already in progress.
353 observer->OnTraceEnabled();
354}
355
358 base::MutexGuard lock(mutex_.get());
359 DCHECK(observers_.find(observer) != observers_.end());
360 observers_.erase(observer);
361}
362#endif // !defined(V8_USE_PERFETTO)
363
364} // namespace tracing
365} // namespace platform
366} // namespace v8
static ThreadTicks Now()
Definition time.cc:797
static TimeTicks Now()
Definition time.cc:736
int64_t ToInternalValue() const
Definition time.h:287
const StringList & GetEnabledCategories() const
Definition v8-tracing.h:202
void UpdateDuration(int64_t timestamp, int64_t cpu_timestamp)
std::unique_ptr< TraceConfig > trace_config_
Definition v8-tracing.h:307
void StartTracing(TraceConfig *trace_config)
void Initialize(TraceBuffer *trace_buffer)
std::unique_ptr< TraceBuffer > trace_buffer_
Definition v8-tracing.h:318
uint64_t AddTraceEvent(char phase, const uint8_t *category_enabled_flag, const char *name, const char *scope, uint64_t id, uint64_t bind_id, int32_t 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) override
const uint8_t * GetCategoryGroupEnabled(const char *category_group) override
std::unique_ptr< base::Mutex > mutex_
Definition v8-tracing.h:306
std::unordered_set< v8::TracingController::TraceStateObserver * > observers_
Definition v8-tracing.h:317
void UpdateCategoryGroupEnabledFlag(size_t category_index)
void RemoveTraceStateObserver(v8::TracingController::TraceStateObserver *observer) override
void AddTraceStateObserver(v8::TracingController::TraceStateObserver *observer) override
void UpdateTraceEventDuration(const uint8_t *category_enabled_flag, const char *name, uint64_t handle) override
uint64_t AddTraceEventWithTimestamp(char phase, const uint8_t *category_enabled_flag, const char *name, const char *scope, uint64_t id, uint64_t bind_id, int32_t 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) override
static const char * GetCategoryGroupName(const uint8_t *category_enabled_flag)
void Relaxed_Store(volatile Atomic8 *ptr, Atomic8 value)
Definition atomicops.h:189
Atomic32 AtomicWord
Definition atomicops.h:76
char Atomic8
Definition atomicops.h:57
Atomic8 Acquire_Load(volatile const Atomic8 *ptr)
Definition atomicops.h:249
void Release_Store(volatile Atomic8 *ptr, Atomic8 value)
Definition atomicops.h:204
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
unsigned char g_category_group_enabled[kMaxCategoryGroups]
const int g_category_categories_exhausted
static const size_t kMaxCategoryGroups
const char * g_category_groups[kMaxCategoryGroups]
v8::base::AtomicWord g_category_index
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK(condition)
Definition logging.h:482