#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/v8-tracing.h"
#include "src/base/atomicops.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/time.h"
#ifdef V8_USE_PERFETTO
#include "perfetto/ext/trace_processor/export_json.h"
#include "perfetto/trace_processor/trace_processor.h"
#include "perfetto/tracing/tracing.h"
#include "protos/perfetto/config/data_source_config.gen.h"
#include "protos/perfetto/config/trace_config.gen.h"
#include "protos/perfetto/config/track_event/track_event_config.gen.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/semaphore.h"
#include "src/libplatform/tracing/trace-event-listener.h"
#endif
#ifdef V8_USE_PERFETTO
class JsonOutputWriter : public perfetto::trace_processor::json::OutputWriter { … };
#endif
namespace v8 {
namespace platform {
namespace tracing {
#if !defined(V8_USE_PERFETTO)
static const size_t kMaxCategoryGroups = 200;
const char* g_category_groups[kMaxCategoryGroups] = {
"toplevel",
"tracing categories exhausted; must increase kMaxCategoryGroups",
"__metadata"};
unsigned char g_category_group_enabled[kMaxCategoryGroups] = {0};
const int g_category_categories_exhausted = 1;
const int g_num_builtin_categories = 3;
v8::base::AtomicWord g_category_index = g_num_builtin_categories;
#endif
TracingController::TracingController() { … }
TracingController::~TracingController() { … }
#ifdef V8_USE_PERFETTO
void TracingController::InitializeForPerfetto(std::ostream* output_stream) { … }
void TracingController::SetTraceEventListenerForTesting(
TraceEventListener* listener) { … }
#else
void TracingController::Initialize(TraceBuffer* trace_buffer) {
trace_buffer_.reset(trace_buffer);
}
int64_t TracingController::CurrentTimestampMicroseconds() {
return base::TimeTicks::Now().ToInternalValue();
}
int64_t TracingController::CurrentCpuTimestampMicroseconds() {
return base::ThreadTicks::Now().ToInternalValue();
}
uint64_t TracingController::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) {
int64_t now_us = CurrentTimestampMicroseconds();
return AddTraceEventWithTimestamp(
phase, category_enabled_flag, name, scope, id, bind_id, num_args,
arg_names, arg_types, arg_values, arg_convertables, flags, now_us);
}
uint64_t TracingController::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) {
int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();
uint64_t handle = 0;
if (recording_.load(std::memory_order_acquire)) {
TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle);
if (trace_object) {
{
base::MutexGuard lock(mutex_.get());
trace_object->Initialize(phase, category_enabled_flag, name, scope, id,
bind_id, num_args, arg_names, arg_types,
arg_values, arg_convertables, flags, timestamp,
cpu_now_us);
}
}
}
return handle;
}
void TracingController::UpdateTraceEventDuration(
const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {
int64_t now_us = CurrentTimestampMicroseconds();
int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();
TraceObject* trace_object = trace_buffer_->GetEventByHandle(handle);
if (!trace_object) return;
trace_object->UpdateDuration(now_us, cpu_now_us);
}
const char* TracingController::GetCategoryGroupName(
const uint8_t* category_group_enabled) {
uintptr_t category_begin =
reinterpret_cast<uintptr_t>(g_category_group_enabled);
uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_group_enabled);
DCHECK(category_ptr >= category_begin &&
category_ptr < reinterpret_cast<uintptr_t>(g_category_group_enabled +
kMaxCategoryGroups));
uintptr_t category_index =
(category_ptr - category_begin) / sizeof(g_category_group_enabled[0]);
return g_category_groups[category_index];
}
#endif
void TracingController::StartTracing(TraceConfig* trace_config) { … }
void TracingController::StopTracing() { … }
#if !defined(V8_USE_PERFETTO)
void TracingController::UpdateCategoryGroupEnabledFlag(size_t category_index) {
unsigned char enabled_flag = 0;
const char* category_group = g_category_groups[category_index];
if (recording_.load(std::memory_order_acquire) &&
trace_config_->IsCategoryGroupEnabled(category_group)) {
enabled_flag |= ENABLED_FOR_RECORDING;
}
if (recording_.load(std::memory_order_acquire) &&
!strcmp(category_group, "__metadata")) {
enabled_flag |= ENABLED_FOR_RECORDING;
}
base::Relaxed_Store(reinterpret_cast<base::Atomic8*>(
g_category_group_enabled + category_index),
enabled_flag);
}
void TracingController::UpdateCategoryGroupEnabledFlags() {
size_t category_index = base::Acquire_Load(&g_category_index);
for (size_t i = 0; i < category_index; i++) UpdateCategoryGroupEnabledFlag(i);
}
const uint8_t* TracingController::GetCategoryGroupEnabled(
const char* category_group) {
DCHECK(!strchr(category_group, '"'));
size_t category_index = base::Acquire_Load(&g_category_index);
for (size_t i = 0; i < category_index; ++i) {
if (strcmp(g_category_groups[i], category_group) == 0) {
return &g_category_group_enabled[i];
}
}
base::MutexGuard lock(mutex_.get());
unsigned char* category_group_enabled = nullptr;
category_index = base::Acquire_Load(&g_category_index);
for (size_t i = 0; i < category_index; ++i) {
if (strcmp(g_category_groups[i], category_group) == 0) {
return &g_category_group_enabled[i];
}
}
DCHECK(category_index < kMaxCategoryGroups);
if (category_index < kMaxCategoryGroups) {
const char* new_group = strdup(category_group);
g_category_groups[category_index] = new_group;
DCHECK(!g_category_group_enabled[category_index]);
UpdateCategoryGroupEnabledFlag(category_index);
category_group_enabled = &g_category_group_enabled[category_index];
base::Release_Store(&g_category_index, category_index + 1);
} else {
category_group_enabled =
&g_category_group_enabled[g_category_categories_exhausted];
}
return category_group_enabled;
}
void TracingController::AddTraceStateObserver(
v8::TracingController::TraceStateObserver* observer) {
{
base::MutexGuard lock(mutex_.get());
observers_.insert(observer);
if (!recording_.load(std::memory_order_acquire)) return;
}
observer->OnTraceEnabled();
}
void TracingController::RemoveTraceStateObserver(
v8::TracingController::TraceStateObserver* observer) {
base::MutexGuard lock(mutex_.get());
DCHECK(observers_.find(observer) != observers_.end());
observers_.erase(observer);
}
#endif
}
}
}