blob: e3115ba2dc9f3e27d914f046d505352e5be3aa2a [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "perfetto/public/abi/track_event_abi.h"
#include "perfetto/public/abi/track_event_hl_abi.h"
#include "perfetto/public/abi/track_event_ll_abi.h"
#include <algorithm>
#include <atomic>
#include <limits>
#include <mutex>
#include <optional>
#include "perfetto/base/compiler.h"
#include "perfetto/base/flat_set.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/no_destructor.h"
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/string_view.h"
#include "perfetto/ext/base/thread_utils.h"
#include "perfetto/protozero/contiguous_memory_range.h"
#include "perfetto/public/compiler.h"
#include "perfetto/tracing/data_source.h"
#include "perfetto/tracing/internal/basic_types.h"
#include "perfetto/tracing/internal/data_source_internal.h"
#include "perfetto/tracing/internal/track_event_internal.h"
#include "perfetto/tracing/track.h"
#include "protos/perfetto/common/data_source_descriptor.gen.h"
#include "protos/perfetto/common/track_event_descriptor.pbzero.h"
#include "protos/perfetto/config/data_source_config.gen.h"
#include "protos/perfetto/config/track_event/track_event_config.gen.h"
#include "protos/perfetto/trace/clock_snapshot.pbzero.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
#include "src/shared_lib/intern_map.h"
#include "src/shared_lib/reset_for_testing.h"
struct PerfettoTeCategoryImpl* perfetto_te_any_categories;
PERFETTO_ATOMIC(bool) * perfetto_te_any_categories_enabled;
uint64_t perfetto_te_process_track_uuid;
struct PerfettoTeCategoryImpl {
std::atomic<bool> flag{false};
std::atomic<uint8_t> instances{0};
PerfettoTeCategoryDescriptor* desc = nullptr;
uint64_t cat_iid = 0;
PerfettoTeCategoryImplCallback cb = nullptr;
void* cb_user_arg = nullptr;
};
enum class MatchType { kExact, kPattern };
static bool NameMatchesPattern(const std::string& pattern,
const perfetto::base::StringView& name,
MatchType match_type) {
// To avoid pulling in all of std::regex, for now we only support a single "*"
// wildcard at the end of the pattern.
size_t i = pattern.find('*');
if (i != std::string::npos) {
if (match_type != MatchType::kPattern)
return false;
return name.substr(0, i) ==
perfetto::base::StringView(pattern).substr(0, i);
}
return name == perfetto::base::StringView(pattern);
}
static bool NameMatchesPatternList(const std::vector<std::string>& patterns,
const perfetto::base::StringView& name,
MatchType match_type) {
for (const auto& pattern : patterns) {
if (NameMatchesPattern(pattern, name, match_type))
return true;
}
return false;
}
static bool IsSingleCategoryEnabled(
const PerfettoTeCategoryDescriptor& c,
const perfetto::protos::gen::TrackEventConfig& config) {
auto has_matching_tag = [&](std::function<bool(const char*)> matcher) {
for (size_t i = 0; i < c.num_tags; ++i) {
if (matcher(c.tags[i]))
return true;
}
return false;
};
// First try exact matches, then pattern matches.
const std::array<MatchType, 2> match_types = {
{MatchType::kExact, MatchType::kPattern}};
for (auto match_type : match_types) {
// 1. Enabled categories.
if (NameMatchesPatternList(config.enabled_categories(), c.name,
match_type)) {
return true;
}
// 2. Enabled tags.
if (has_matching_tag([&](const char* tag) {
return NameMatchesPatternList(config.enabled_tags(), tag, match_type);
})) {
return true;
}
// 3. Disabled categories.
if (NameMatchesPatternList(config.disabled_categories(), c.name,
match_type)) {
return false;
}
// 4. Disabled tags.
if (has_matching_tag([&](const char* tag) {
return NameMatchesPatternList(config.disabled_tags(), tag,
match_type);
})) {
return false;
}
}
// If nothing matched, the category is disabled by default. N.B. this behavior
// is different than the C++ TrackEvent API.
return false;
}
static bool IsRegisteredCategoryEnabled(
const PerfettoTeCategoryImpl& cat,
const perfetto::protos::gen::TrackEventConfig& config) {
if (!cat.desc) {
return false;
}
return IsSingleCategoryEnabled(*cat.desc, config);
}
static void EnableRegisteredCategory(PerfettoTeCategoryImpl* cat,
uint32_t instance_index) {
PERFETTO_DCHECK(instance_index < perfetto::internal::kMaxDataSourceInstances);
// Matches the acquire_load in DataSource::Trace().
uint8_t old = cat->instances.fetch_or(
static_cast<uint8_t>(1u << instance_index), std::memory_order_release);
bool global_state_changed = old == 0;
if (global_state_changed) {
cat->flag.store(true, std::memory_order_relaxed);
}
if (cat->cb) {
cat->cb(cat, instance_index, /*created=*/true, global_state_changed,
cat->cb_user_arg);
}
}
static void DisableRegisteredCategory(PerfettoTeCategoryImpl* cat,
uint32_t instance_index) {
PERFETTO_DCHECK(instance_index < perfetto::internal::kMaxDataSourceInstances);
// Matches the acquire_load in DataSource::Trace().
cat->instances.fetch_and(static_cast<uint8_t>(~(1u << instance_index)),
std::memory_order_release);
bool global_state_changed = false;
if (!cat->instances.load(std::memory_order_relaxed)) {
cat->flag.store(false, std::memory_order_relaxed);
global_state_changed = true;
}
if (cat->cb) {
cat->cb(cat, instance_index, /*created=*/false, global_state_changed,
cat->cb_user_arg);
}
}
static void SerializeCategory(
const PerfettoTeCategoryDescriptor& desc,
perfetto::protos::pbzero::TrackEventDescriptor* ted) {
auto* c = ted->add_available_categories();
c->set_name(desc.name);
if (desc.desc)
c->set_description(desc.desc);
for (size_t j = 0; j < desc.num_tags; ++j) {
c->add_tags(desc.tags[j]);
}
}
namespace perfetto {
namespace shlib {
struct TrackEventIncrementalState {
// A heap-allocated message for storing newly seen interned data while we are
// in the middle of writing a track event. When a track event wants to write
// new interned data into the trace, it is first serialized into this message
// and then flushed to the real trace in EventContext when the packet ends.
// The message is cached here as a part of incremental state so that we can
// reuse the underlying buffer allocation for subsequently written interned
// data.
uint64_t last_timestamp_ns = 0;
protozero::HeapBuffered<protos::pbzero::InternedData>
serialized_interned_data;
bool was_cleared = true;
base::FlatSet<uint64_t> seen_track_uuids;
// Map from serialized representation of a dynamic category to its enabled
// state.
base::FlatHashMap<std::string, bool> dynamic_categories;
InternMap iids;
};
struct TrackEventTlsState {
template <typename TraceContext>
explicit TrackEventTlsState(const TraceContext& trace_context) {
auto locked_ds = trace_context.GetDataSourceLocked();
bool disable_incremental_timestamps = false;
timestamp_unit_multiplier = 1;
if (locked_ds.valid()) {
const auto& config = locked_ds->GetConfig();
disable_incremental_timestamps = config.disable_incremental_timestamps();
if (config.has_timestamp_unit_multiplier() &&
config.timestamp_unit_multiplier() != 0) {
timestamp_unit_multiplier = config.timestamp_unit_multiplier();
}
}
if (disable_incremental_timestamps) {
if (timestamp_unit_multiplier == 1) {
default_clock_id = PERFETTO_I_CLOCK_INCREMENTAL_UNDERNEATH;
} else {
default_clock_id = PERFETTO_TE_TIMESTAMP_TYPE_ABSOLUTE;
}
} else {
default_clock_id = PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL;
}
}
uint32_t default_clock_id;
uint64_t timestamp_unit_multiplier;
};
struct TrackEventDataSourceTraits : public perfetto::DefaultDataSourceTraits {
using IncrementalStateType = TrackEventIncrementalState;
using TlsStateType = TrackEventTlsState;
};
class TrackEvent
: public perfetto::DataSource<TrackEvent, TrackEventDataSourceTraits> {
public:
~TrackEvent() override;
void OnSetup(const DataSourceBase::SetupArgs& args) override {
const std::string& config_raw = args.config->track_event_config_raw();
bool ok = config_.ParseFromArray(config_raw.data(), config_raw.size());
if (!ok) {
PERFETTO_LOG("Failed to parse config");
}
inst_id_ = args.internal_instance_index;
}
void OnStart(const DataSourceBase::StartArgs&) override {
GlobalState::Instance().OnStart(config_, inst_id_);
}
void OnStop(const DataSourceBase::StopArgs&) override {
GlobalState::Instance().OnStop(inst_id_);
}
const perfetto::protos::gen::TrackEventConfig& GetConfig() const {
return config_;
}
static void Init() {
DataSourceDescriptor dsd =
GlobalState::Instance().GenerateDescriptorFromCategories();
Register(dsd);
}
static void RegisterCategory(PerfettoTeCategoryImpl* cat) {
GlobalState::Instance().RegisterCategory(cat);
}
static void UpdateDescriptorFromCategories() {
DataSourceDescriptor dsd =
GlobalState::Instance().GenerateDescriptorFromCategories();
UpdateDescriptor(dsd);
}
static void UnregisterCategory(PerfettoTeCategoryImpl* cat) {
GlobalState::Instance().UnregisterCategory(cat);
}
static void CategorySetCallback(struct PerfettoTeCategoryImpl* cat,
PerfettoTeCategoryImplCallback cb,
void* user_arg) {
GlobalState::Instance().CategorySetCallback(cat, cb, user_arg);
}
static internal::DataSourceType* GetType();
static internal::DataSourceThreadLocalState** GetTlsState();
private:
struct GlobalState {
static GlobalState& Instance() {
static GlobalState* instance = new GlobalState();
return *instance;
}
void OnStart(const perfetto::protos::gen::TrackEventConfig& config,
uint32_t instance_id) {
std::lock_guard<std::mutex> lock(mu_);
EnableRegisteredCategory(perfetto_te_any_categories, instance_id);
for (PerfettoTeCategoryImpl* cat : categories_) {
if (IsRegisteredCategoryEnabled(*cat, config)) {
EnableRegisteredCategory(cat, instance_id);
}
}
}
void OnStop(uint32_t instance_id) {
std::lock_guard<std::mutex> lock(GlobalState::Instance().mu_);
for (PerfettoTeCategoryImpl* cat : GlobalState::Instance().categories_) {
DisableRegisteredCategory(cat, instance_id);
}
DisableRegisteredCategory(perfetto_te_any_categories, instance_id);
}
void RegisterCategory(PerfettoTeCategoryImpl* cat) {
{
std::lock_guard<std::mutex> lock(mu_);
Trace([cat](TraceContext ctx) {
auto ds = ctx.GetDataSourceLocked();
if (IsRegisteredCategoryEnabled(*cat, ds->GetConfig())) {
EnableRegisteredCategory(cat, ds->inst_id_);
}
});
categories_.push_back(cat);
cat->cat_iid = ++GlobalState::Instance().interned_categories_;
}
}
void UnregisterCategory(PerfettoTeCategoryImpl* cat) {
std::lock_guard<std::mutex> lock(mu_);
categories_.erase(
std::remove(categories_.begin(), categories_.end(), cat),
categories_.end());
}
void CategorySetCallback(struct PerfettoTeCategoryImpl* cat,
PerfettoTeCategoryImplCallback cb,
void* user_arg) {
std::lock_guard<std::mutex> lock(mu_);
cat->cb = cb;
cat->cb_user_arg = user_arg;
if (!cat->cb) {
return;
}
bool first = true;
uint8_t active_instances = cat->instances.load(std::memory_order_relaxed);
for (PerfettoDsInstanceIndex i = 0; i < internal::kMaxDataSourceInstances;
i++) {
if ((active_instances & (1 << i)) == 0) {
continue;
}
cb(cat, i, true, first, user_arg);
first = false;
}
}
DataSourceDescriptor GenerateDescriptorFromCategories() const {
DataSourceDescriptor dsd;
dsd.set_name("track_event");
protozero::HeapBuffered<perfetto::protos::pbzero::TrackEventDescriptor>
ted;
for (PerfettoTeCategoryImpl* cat : categories_) {
SerializeCategory(*cat->desc, ted.get());
}
dsd.set_track_event_descriptor_raw(ted.SerializeAsString());
return dsd;
}
private:
GlobalState() : interned_categories_(0) {
perfetto_te_any_categories = new PerfettoTeCategoryImpl;
perfetto_te_any_categories_enabled = &perfetto_te_any_categories->flag;
}
// Guards categories and interned_categories;
std::mutex mu_;
std::vector<PerfettoTeCategoryImpl*> categories_;
uint64_t interned_categories_;
};
uint32_t inst_id_;
perfetto::protos::gen::TrackEventConfig config_;
};
TrackEvent::~TrackEvent() = default;
void ResetTrackEventTls() {
*TrackEvent::GetTlsState() = nullptr;
}
struct TracePointTraits {
struct TracePointData {
struct PerfettoTeCategoryImpl* enabled;
};
static constexpr std::atomic<uint8_t>* GetActiveInstances(
TracePointData data) {
return &data.enabled->instances;
}
};
} // namespace shlib
} // namespace perfetto
PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(
perfetto::shlib::TrackEvent,
perfetto::shlib::TrackEventDataSourceTraits);
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(
perfetto::shlib::TrackEvent,
perfetto::shlib::TrackEventDataSourceTraits);
perfetto::internal::DataSourceType* perfetto::shlib::TrackEvent::GetType() {
return &perfetto::shlib::TrackEvent::Helper::type();
}
perfetto::internal::DataSourceThreadLocalState**
perfetto::shlib::TrackEvent::GetTlsState() {
return &tls_state_;
}
namespace {
using perfetto::internal::TrackEventInternal;
perfetto::protos::pbzero::TrackEvent::Type EventType(int32_t type) {
using Type = perfetto::protos::pbzero::TrackEvent::Type;
auto enum_type = static_cast<PerfettoTeType>(type);
switch (enum_type) {
case PERFETTO_TE_TYPE_SLICE_BEGIN:
return Type::TYPE_SLICE_BEGIN;
case PERFETTO_TE_TYPE_SLICE_END:
return Type::TYPE_SLICE_END;
case PERFETTO_TE_TYPE_INSTANT:
return Type::TYPE_INSTANT;
case PERFETTO_TE_TYPE_COUNTER:
return Type::TYPE_COUNTER;
}
return Type::TYPE_UNSPECIFIED;
}
protozero::MessageHandle<perfetto::protos::pbzero::TracePacket>
NewTracePacketInternal(perfetto::TraceWriterBase* trace_writer,
perfetto::shlib::TrackEventIncrementalState* incr_state,
const perfetto::shlib::TrackEventTlsState& tls_state,
perfetto::TraceTimestamp timestamp,
uint32_t seq_flags) {
// PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL is the default timestamp returned
// by TrackEventInternal::GetTraceTime(). If the configuration in `tls_state`
// uses a different clock, we have to use that instead.
if (PERFETTO_UNLIKELY(tls_state.default_clock_id !=
PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL &&
timestamp.clock_id ==
PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL)) {
timestamp.clock_id = tls_state.default_clock_id;
}
auto packet = trace_writer->NewTracePacket();
auto ts_unit_multiplier = tls_state.timestamp_unit_multiplier;
if (PERFETTO_LIKELY(timestamp.clock_id ==
PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL)) {
if (PERFETTO_LIKELY(incr_state->last_timestamp_ns <= timestamp.value)) {
// No need to set the clock id here, since
// PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL is the clock id assumed by
// default.
auto time_diff_ns = timestamp.value - incr_state->last_timestamp_ns;
auto time_diff_units = time_diff_ns / ts_unit_multiplier;
packet->set_timestamp(time_diff_units);
incr_state->last_timestamp_ns += time_diff_units * ts_unit_multiplier;
} else {
packet->set_timestamp(timestamp.value / ts_unit_multiplier);
packet->set_timestamp_clock_id(
ts_unit_multiplier == 1
? static_cast<uint32_t>(PERFETTO_I_CLOCK_INCREMENTAL_UNDERNEATH)
: static_cast<uint32_t>(PERFETTO_TE_TIMESTAMP_TYPE_ABSOLUTE));
}
} else if (PERFETTO_LIKELY(timestamp.clock_id ==
tls_state.default_clock_id)) {
packet->set_timestamp(timestamp.value / ts_unit_multiplier);
} else {
packet->set_timestamp(timestamp.value);
packet->set_timestamp_clock_id(timestamp.clock_id);
}
packet->set_sequence_flags(seq_flags);
return packet;
}
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
std::vector<std::string> GetCmdLine() {
std::vector<std::string> cmdline_str;
std::string cmdline;
if (perfetto::base::ReadFile("/proc/self/cmdline", &cmdline)) {
perfetto::base::StringSplitter splitter(std::move(cmdline), '\0');
while (splitter.Next()) {
cmdline_str.emplace_back(splitter.cur_token(), splitter.cur_token_size());
}
}
return cmdline_str;
}
#endif
void ResetIncrementalStateIfRequired(
perfetto::TraceWriterBase* trace_writer,
perfetto::shlib::TrackEventIncrementalState* incr_state,
const perfetto::shlib::TrackEventTlsState& tls_state,
const perfetto::TraceTimestamp& timestamp) {
if (!incr_state->was_cleared) {
return;
}
incr_state->was_cleared = false;
auto sequence_timestamp = timestamp;
if (timestamp.clock_id != PERFETTO_I_CLOCK_INCREMENTAL_UNDERNEATH &&
timestamp.clock_id != PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL) {
sequence_timestamp = TrackEventInternal::GetTraceTime();
}
incr_state->last_timestamp_ns = sequence_timestamp.value;
auto tid = perfetto::base::GetThreadId();
auto pid = perfetto::Platform::GetCurrentProcessId();
uint64_t thread_track_uuid =
perfetto_te_process_track_uuid ^ static_cast<uint64_t>(tid);
auto ts_unit_multiplier = tls_state.timestamp_unit_multiplier;
{
// Mark any incremental state before this point invalid. Also set up
// defaults so that we don't need to repeat constant data for each packet.
auto packet = NewTracePacketInternal(
trace_writer, incr_state, tls_state, timestamp,
perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
auto defaults = packet->set_trace_packet_defaults();
defaults->set_timestamp_clock_id(tls_state.default_clock_id);
// Establish the default track for this event sequence.
auto track_defaults = defaults->set_track_event_defaults();
track_defaults->set_track_uuid(thread_track_uuid);
if (tls_state.default_clock_id != PERFETTO_I_CLOCK_INCREMENTAL_UNDERNEATH) {
perfetto::protos::pbzero::ClockSnapshot* clocks =
packet->set_clock_snapshot();
// Trace clock.
perfetto::protos::pbzero::ClockSnapshot::Clock* trace_clock =
clocks->add_clocks();
trace_clock->set_clock_id(PERFETTO_I_CLOCK_INCREMENTAL_UNDERNEATH);
trace_clock->set_timestamp(sequence_timestamp.value);
if (PERFETTO_LIKELY(tls_state.default_clock_id ==
PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL)) {
// Delta-encoded incremental clock in nanoseconds by default but
// configurable by |tls_state.timestamp_unit_multiplier|.
perfetto::protos::pbzero::ClockSnapshot::Clock* clock_incremental =
clocks->add_clocks();
clock_incremental->set_clock_id(PERFETTO_TE_TIMESTAMP_TYPE_INCREMENTAL);
clock_incremental->set_timestamp(sequence_timestamp.value /
ts_unit_multiplier);
clock_incremental->set_is_incremental(true);
clock_incremental->set_unit_multiplier_ns(ts_unit_multiplier);
}
if (ts_unit_multiplier > 1) {
// absolute clock with custom timestamp_unit_multiplier.
perfetto::protos::pbzero::ClockSnapshot::Clock* absolute_clock =
clocks->add_clocks();
absolute_clock->set_clock_id(PERFETTO_TE_TIMESTAMP_TYPE_ABSOLUTE);
absolute_clock->set_timestamp(sequence_timestamp.value /
ts_unit_multiplier);
absolute_clock->set_is_incremental(false);
absolute_clock->set_unit_multiplier_ns(ts_unit_multiplier);
}
}
}
// Every thread should write a descriptor for its default track, because most
// trace points won't explicitly reference it. We also write the process
// descriptor from every thread that writes trace events to ensure it gets
// emitted at least once.
{
auto packet = NewTracePacketInternal(
trace_writer, incr_state, tls_state, timestamp,
perfetto::protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
auto* track = packet->set_track_descriptor();
track->set_uuid(thread_track_uuid);
track->set_parent_uuid(perfetto_te_process_track_uuid);
auto* td = track->set_thread();
td->set_pid(static_cast<int32_t>(pid));
td->set_tid(static_cast<int32_t>(tid));
std::string thread_name;
if (perfetto::base::GetThreadName(thread_name))
td->set_thread_name(thread_name);
}
{
auto packet = NewTracePacketInternal(
trace_writer, incr_state, tls_state, timestamp,
perfetto::protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
auto* track = packet->set_track_descriptor();
track->set_uuid(perfetto_te_process_track_uuid);
auto* pd = track->set_process();
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
static perfetto::base::NoDestructor<std::vector<std::string>> cmdline(
GetCmdLine());
if (!cmdline.ref().empty()) {
// Since cmdline is a zero-terminated list of arguments, this ends up
// writing just the first element, i.e., the process name, into the
// process name field.
pd->set_process_name(cmdline.ref()[0]);
for (const std::string& arg : cmdline.ref()) {
pd->add_cmdline(arg);
}
}
#endif
pd->set_pid(static_cast<int32_t>(pid));
}
}
// Appends the fields described by `fields` to `msg`.
void AppendHlProtoFields(protozero::Message* msg,
PerfettoTeHlProtoField* const* fields) {
for (PerfettoTeHlProtoField* const* p = fields; *p != nullptr; p++) {
switch ((*p)->type) {
case PERFETTO_TE_HL_PROTO_TYPE_CSTR: {
auto field = reinterpret_cast<PerfettoTeHlProtoFieldCstr*>(*p);
msg->AppendString(field->header.id, field->str);
break;
}
case PERFETTO_TE_HL_PROTO_TYPE_BYTES: {
auto field = reinterpret_cast<PerfettoTeHlProtoFieldBytes*>(*p);
msg->AppendBytes(field->header.id, field->buf, field->len);
break;
}
case PERFETTO_TE_HL_PROTO_TYPE_NESTED: {
auto field = reinterpret_cast<PerfettoTeHlProtoFieldNested*>(*p);
auto* nested =
msg->BeginNestedMessage<protozero::Message>(field->header.id);
AppendHlProtoFields(nested, field->fields);
break;
}
case PERFETTO_TE_HL_PROTO_TYPE_VARINT: {
auto field = reinterpret_cast<PerfettoTeHlProtoFieldVarInt*>(*p);
msg->AppendVarInt(field->header.id, field->value);
break;
}
case PERFETTO_TE_HL_PROTO_TYPE_FIXED64: {
auto field = reinterpret_cast<PerfettoTeHlProtoFieldFixed64*>(*p);
msg->AppendFixed(field->header.id, field->value);
break;
}
case PERFETTO_TE_HL_PROTO_TYPE_FIXED32: {
auto field = reinterpret_cast<PerfettoTeHlProtoFieldFixed32*>(*p);
msg->AppendFixed(field->header.id, field->value);
break;
}
case PERFETTO_TE_HL_PROTO_TYPE_DOUBLE: {
auto field = reinterpret_cast<PerfettoTeHlProtoFieldDouble*>(*p);
msg->AppendFixed(field->header.id, field->value);
break;
}
case PERFETTO_TE_HL_PROTO_TYPE_FLOAT: {
auto field = reinterpret_cast<PerfettoTeHlProtoFieldFloat*>(*p);
msg->AppendFixed(field->header.id, field->value);
break;
}
}
}
}
void WriteTrackEvent(perfetto::shlib::TrackEventIncrementalState* incr,
perfetto::protos::pbzero::TrackEvent* event,
PerfettoTeCategoryImpl* cat,
perfetto::protos::pbzero::TrackEvent::Type type,
const char* name,
const PerfettoTeHlExtra* const* extra_data,
std::optional<uint64_t> track_uuid,
const PerfettoTeCategoryDescriptor* dynamic_cat,
bool use_interning) {
if (type != perfetto::protos::pbzero::TrackEvent::TYPE_UNSPECIFIED) {
event->set_type(type);
}
if (!dynamic_cat &&
type != perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END &&
type != perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER) {
uint64_t iid = cat->cat_iid;
auto res = incr->iids.FindOrAssign(
perfetto::protos::pbzero::InternedData::kEventCategoriesFieldNumber,
&iid, sizeof(iid));
if (res.newly_assigned) {
auto* ser = incr->serialized_interned_data->add_event_categories();
ser->set_iid(iid);
ser->set_name(cat->desc->name);
}
event->add_category_iids(iid);
}
if (type != perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END) {
if (name) {
if (use_interning) {
const void* str = name;
size_t len = strlen(name);
auto res = incr->iids.FindOrAssign(
perfetto::protos::pbzero::InternedData::kEventNamesFieldNumber, str,
len);
if (res.newly_assigned) {
auto* ser = incr->serialized_interned_data->add_event_names();
ser->set_iid(res.iid);
ser->set_name(name);
}
event->set_name_iid(res.iid);
} else {
event->set_name(name);
}
}
}
if (dynamic_cat &&
type != perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END &&
type != perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER) {
event->add_categories(dynamic_cat->name);
}
if (track_uuid) {
event->set_track_uuid(*track_uuid);
}
for (const auto* it = extra_data; *it != nullptr; it++) {
const struct PerfettoTeHlExtra& extra = **it;
if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_INT64 &&
type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER) {
event->set_counter_value(
reinterpret_cast<const struct PerfettoTeHlExtraCounterInt64&>(extra)
.value);
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_DOUBLE) {
event->set_double_counter_value(
reinterpret_cast<const struct PerfettoTeHlExtraCounterDouble&>(extra)
.value);
}
}
for (const auto* it = extra_data; *it != nullptr; it++) {
const struct PerfettoTeHlExtra& extra = **it;
if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_BOOL ||
extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_UINT64 ||
extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_INT64 ||
extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_DOUBLE ||
extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_STRING ||
extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_POINTER) {
auto* dbg = event->add_debug_annotations();
const char* arg_name = nullptr;
if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_BOOL) {
const auto& arg =
reinterpret_cast<const struct PerfettoTeHlExtraDebugArgBool&>(
extra);
dbg->set_bool_value(arg.value);
arg_name = arg.name;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_UINT64) {
const auto& arg =
reinterpret_cast<const struct PerfettoTeHlExtraDebugArgUint64&>(
extra);
dbg->set_uint_value(arg.value);
arg_name = arg.name;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_INT64) {
const auto& arg =
reinterpret_cast<const struct PerfettoTeHlExtraDebugArgInt64&>(
extra);
dbg->set_int_value(arg.value);
arg_name = arg.name;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_DOUBLE) {
const auto& arg =
reinterpret_cast<const struct PerfettoTeHlExtraDebugArgDouble&>(
extra);
dbg->set_double_value(arg.value);
arg_name = arg.name;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_STRING) {
const auto& arg =
reinterpret_cast<const struct PerfettoTeHlExtraDebugArgString&>(
extra);
dbg->set_string_value(arg.value);
arg_name = arg.name;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_POINTER) {
const auto& arg =
reinterpret_cast<const struct PerfettoTeHlExtraDebugArgPointer&>(
extra);
dbg->set_pointer_value(arg.value);
arg_name = arg.name;
}
if (arg_name != nullptr) {
const void* str = arg_name;
size_t len = strlen(arg_name);
auto res =
incr->iids.FindOrAssign(perfetto::protos::pbzero::InternedData::
kDebugAnnotationNamesFieldNumber,
str, len);
if (res.newly_assigned) {
auto* ser =
incr->serialized_interned_data->add_debug_annotation_names();
ser->set_iid(res.iid);
ser->set_name(arg_name);
}
dbg->set_name_iid(res.iid);
}
}
}
for (const auto* it = extra_data; *it != nullptr; it++) {
const struct PerfettoTeHlExtra& extra = **it;
if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_FLOW) {
event->add_flow_ids(
reinterpret_cast<const struct PerfettoTeHlExtraFlow&>(extra).id);
}
}
for (const auto* it = extra_data; *it != nullptr; it++) {
const struct PerfettoTeHlExtra& extra = **it;
if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_TERMINATING_FLOW) {
event->add_terminating_flow_ids(
reinterpret_cast<const struct PerfettoTeHlExtraFlow&>(extra).id);
}
}
for (const auto* it = extra_data; *it != nullptr; it++) {
const struct PerfettoTeHlExtra& extra = **it;
if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_PROTO_FIELDS) {
const auto* fields =
reinterpret_cast<const struct PerfettoTeHlExtraProtoFields&>(extra)
.fields;
AppendHlProtoFields(event, fields);
}
}
}
} // namespace
struct PerfettoTeCategoryImpl* PerfettoTeCategoryImplCreate(
struct PerfettoTeCategoryDescriptor* desc) {
auto* cat = new PerfettoTeCategoryImpl;
cat->desc = desc;
perfetto::shlib::TrackEvent::RegisterCategory(cat);
return cat;
}
void PerfettoTePublishCategories() {
perfetto::shlib::TrackEvent::UpdateDescriptorFromCategories();
}
void PerfettoTeCategoryImplSetCallback(struct PerfettoTeCategoryImpl* cat,
PerfettoTeCategoryImplCallback cb,
void* user_arg) {
perfetto::shlib::TrackEvent::CategorySetCallback(cat, cb, user_arg);
}
PERFETTO_ATOMIC(bool) *
PerfettoTeCategoryImplGetEnabled(struct PerfettoTeCategoryImpl* cat) {
return &cat->flag;
}
uint64_t PerfettoTeCategoryImplGetIid(struct PerfettoTeCategoryImpl* cat) {
return cat->cat_iid;
}
void PerfettoTeCategoryImplDestroy(struct PerfettoTeCategoryImpl* cat) {
perfetto::shlib::TrackEvent::UnregisterCategory(cat);
delete cat;
}
void PerfettoTeInit(void) {
perfetto::shlib::TrackEvent::Init();
perfetto_te_process_track_uuid =
perfetto::internal::TrackRegistry::ComputeProcessUuid();
}
struct PerfettoTeTimestamp PerfettoTeGetTimestamp(void) {
struct PerfettoTeTimestamp ret;
ret.clock_id = PERFETTO_TE_TIMESTAMP_TYPE_BOOT;
ret.value = TrackEventInternal::GetTimeNs();
return ret;
}
static bool IsDynamicCategoryEnabled(
uint32_t inst_idx,
perfetto::shlib::TrackEventIncrementalState* incr_state,
const struct PerfettoTeCategoryDescriptor& desc) {
constexpr size_t kMaxCacheSize = 20;
perfetto::internal::DataSourceType* ds =
perfetto::shlib::TrackEvent::GetType();
auto& cache = incr_state->dynamic_categories;
protozero::HeapBuffered<perfetto::protos::pbzero::TrackEventDescriptor> ted;
SerializeCategory(desc, ted.get());
std::string serialized = ted.SerializeAsString();
auto* cached = cache.Find(serialized);
if (cached) {
return *cached;
}
auto* internal_state = ds->static_state()->TryGet(inst_idx);
if (!internal_state)
return false;
std::unique_lock<std::recursive_mutex> lock(internal_state->lock);
auto* sds = static_cast<perfetto::shlib::TrackEvent*>(
internal_state->data_source.get());
bool res = IsSingleCategoryEnabled(desc, sds->GetConfig());
if (cache.size() < kMaxCacheSize) {
cache[serialized] = res;
}
return res;
}
// If the category `dyn_cat` is enabled on the data source instance pointed by
// `ii`, returns immediately. Otherwise, advances `ii` to a data source instance
// where `dyn_cat` is enabled. If there's no data source instance where
// `dyn_cat` is enabled, `ii->instance` will be nullptr.
static void AdvanceToFirstEnabledDynamicCategory(
perfetto::internal::DataSourceType::InstancesIterator* ii,
perfetto::internal::DataSourceThreadLocalState* tls_state,
struct PerfettoTeCategoryImpl* cat,
const PerfettoTeCategoryDescriptor& dyn_cat) {
perfetto::internal::DataSourceType* ds =
perfetto::shlib::TrackEvent::GetType();
for (; ii->instance;
ds->NextIteration</*Traits=*/perfetto::shlib::TracePointTraits>(
ii, tls_state, {cat})) {
auto* incr_state =
static_cast<perfetto::shlib::TrackEventIncrementalState*>(
ds->GetIncrementalState(ii->instance, ii->i));
if (IsDynamicCategoryEnabled(ii->i, incr_state, dyn_cat)) {
break;
}
}
}
static void InstanceOp(
perfetto::internal::DataSourceType* ds,
perfetto::internal::DataSourceType::InstancesIterator* ii,
perfetto::internal::DataSourceThreadLocalState* tls_state,
struct PerfettoTeCategoryImpl* cat,
perfetto::protos::pbzero::TrackEvent::Type type,
const char* name,
struct PerfettoTeHlExtra* const* extra_data) {
if (!ii->instance) {
return;
}
const PerfettoTeRegisteredTrackImpl* registered_track = nullptr;
const PerfettoTeHlExtraNamedTrack* named_track = nullptr;
std::optional<uint64_t> track_uuid;
const struct PerfettoTeHlExtraTimestamp* custom_timestamp = nullptr;
const struct PerfettoTeCategoryDescriptor* dynamic_cat = nullptr;
std::optional<int64_t> int_counter;
std::optional<double> double_counter;
bool use_interning = true;
bool flush = false;
for (const auto* it = extra_data; *it != nullptr; it++) {
const struct PerfettoTeHlExtra& extra = **it;
if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_REGISTERED_TRACK) {
const auto& cast =
reinterpret_cast<const struct PerfettoTeHlExtraRegisteredTrack&>(
extra);
registered_track = cast.track;
named_track = nullptr;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_NAMED_TRACK) {
registered_track = nullptr;
named_track =
&reinterpret_cast<const struct PerfettoTeHlExtraNamedTrack&>(extra);
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_TIMESTAMP) {
custom_timestamp =
&reinterpret_cast<const struct PerfettoTeHlExtraTimestamp&>(extra);
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_DYNAMIC_CATEGORY) {
dynamic_cat =
reinterpret_cast<const struct PerfettoTeHlExtraDynamicCategory&>(
extra)
.desc;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_INT64) {
int_counter =
reinterpret_cast<const struct PerfettoTeHlExtraCounterInt64&>(extra)
.value;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_DOUBLE) {
double_counter =
reinterpret_cast<const struct PerfettoTeHlExtraCounterInt64&>(extra)
.value;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_NO_INTERN) {
use_interning = false;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_FLUSH) {
flush = true;
}
}
perfetto::TraceTimestamp ts;
if (custom_timestamp) {
ts.clock_id = custom_timestamp->timestamp.clock_id;
ts.value = custom_timestamp->timestamp.value;
} else {
ts = TrackEventInternal::GetTraceTime();
}
if (PERFETTO_UNLIKELY(dynamic_cat)) {
AdvanceToFirstEnabledDynamicCategory(ii, tls_state, cat, *dynamic_cat);
if (!ii->instance) {
return;
}
}
const auto& track_event_tls =
*static_cast<perfetto::shlib::TrackEventTlsState*>(
ii->instance->data_source_custom_tls.get());
auto* incr_state = static_cast<perfetto::shlib::TrackEventIncrementalState*>(
ds->GetIncrementalState(ii->instance, ii->i));
ResetIncrementalStateIfRequired(ii->instance->trace_writer.get(), incr_state,
track_event_tls, ts);
if (registered_track) {
if (incr_state->seen_track_uuids.insert(registered_track->uuid).second) {
auto packet = ii->instance->trace_writer->NewTracePacket();
auto* track_descriptor = packet->set_track_descriptor();
track_descriptor->AppendRawProtoBytes(registered_track->descriptor,
registered_track->descriptor_size);
}
track_uuid = registered_track->uuid;
} else if (named_track) {
uint64_t uuid = named_track->parent_uuid;
uuid ^= PerfettoFnv1a(named_track->name, strlen(named_track->name));
uuid ^= named_track->id;
if (incr_state->seen_track_uuids.insert(uuid).second) {
auto packet = ii->instance->trace_writer->NewTracePacket();
auto* track_descriptor = packet->set_track_descriptor();
track_descriptor->set_uuid(uuid);
if (named_track->parent_uuid) {
track_descriptor->set_parent_uuid(named_track->parent_uuid);
}
track_descriptor->set_name(named_track->name);
}
track_uuid = uuid;
}
perfetto::TraceWriterBase* trace_writer = ii->instance->trace_writer.get();
{
auto packet = NewTracePacketInternal(
trace_writer, incr_state, track_event_tls, ts,
perfetto::protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
auto* track_event = packet->set_track_event();
WriteTrackEvent(incr_state, track_event, cat, type, name, extra_data,
track_uuid, dynamic_cat, use_interning);
track_event->Finalize();
if (!incr_state->serialized_interned_data.empty()) {
auto ranges = incr_state->serialized_interned_data.GetRanges();
packet->AppendScatteredBytes(
perfetto::protos::pbzero::TracePacket::kInternedDataFieldNumber,
ranges.data(), ranges.size());
incr_state->serialized_interned_data.Reset();
}
}
if (PERFETTO_UNLIKELY(flush)) {
trace_writer->Flush();
}
}
void PerfettoTeHlEmitImpl(struct PerfettoTeCategoryImpl* cat,
int32_t type,
const char* name,
struct PerfettoTeHlExtra* const* extra_data) {
uint32_t cached_instances =
perfetto::shlib::TracePointTraits::GetActiveInstances({cat})->load(
std::memory_order_relaxed);
if (!cached_instances) {
return;
}
perfetto::internal::DataSourceType* ds =
perfetto::shlib::TrackEvent::GetType();
perfetto::internal::DataSourceThreadLocalState*& tls_state =
*perfetto::shlib::TrackEvent::GetTlsState();
if (!ds->TracePrologue<perfetto::shlib::TrackEventDataSourceTraits,
perfetto::shlib::TracePointTraits>(
&tls_state, &cached_instances, {cat})) {
return;
}
for (perfetto::internal::DataSourceType::InstancesIterator ii =
ds->BeginIteration<perfetto::shlib::TracePointTraits>(
cached_instances, tls_state, {cat});
ii.instance;
ds->NextIteration</*Traits=*/perfetto::shlib::TracePointTraits>(
&ii, tls_state, {cat})) {
InstanceOp(ds, &ii, tls_state, cat, EventType(type), name, extra_data);
}
ds->TraceEpilogue(tls_state);
}
static void FillIterator(
const perfetto::internal::DataSourceType::InstancesIterator* ii,
struct PerfettoTeTimestamp ts,
struct PerfettoTeLlImplIterator* iterator) {
perfetto::internal::DataSourceType* ds =
perfetto::shlib::TrackEvent::GetType();
auto& track_event_tls = *static_cast<perfetto::shlib::TrackEventTlsState*>(
ii->instance->data_source_custom_tls.get());
auto* incr_state = static_cast<perfetto::shlib::TrackEventIncrementalState*>(
ds->GetIncrementalState(ii->instance, ii->i));
perfetto::TraceTimestamp tts;
tts.clock_id = ts.clock_id;
tts.value = ts.value;
ResetIncrementalStateIfRequired(ii->instance->trace_writer.get(), incr_state,
track_event_tls, tts);
iterator->incr = reinterpret_cast<struct PerfettoTeLlImplIncr*>(incr_state);
iterator->tls =
reinterpret_cast<struct PerfettoTeLlImplTls*>(&track_event_tls);
}
struct PerfettoTeLlImplIterator PerfettoTeLlImplBegin(
struct PerfettoTeCategoryImpl* cat,
struct PerfettoTeTimestamp ts) {
struct PerfettoTeLlImplIterator ret = {};
uint32_t cached_instances =
perfetto::shlib::TracePointTraits::GetActiveInstances({cat})->load(
std::memory_order_relaxed);
if (!cached_instances) {
return ret;
}
perfetto::internal::DataSourceType* ds =
perfetto::shlib::TrackEvent::GetType();
perfetto::internal::DataSourceThreadLocalState*& tls_state =
*perfetto::shlib::TrackEvent::GetTlsState();
if (!ds->TracePrologue<perfetto::shlib::TrackEventDataSourceTraits,
perfetto::shlib::TracePointTraits>(
&tls_state, &cached_instances, {cat})) {
return ret;
}
perfetto::internal::DataSourceType::InstancesIterator ii =
ds->BeginIteration<perfetto::shlib::TracePointTraits>(cached_instances,
tls_state, {cat});
ret.ds.inst_id = ii.i;
tls_state->root_tls->cached_instances = ii.cached_instances;
ret.ds.tracer = reinterpret_cast<struct PerfettoDsTracerImpl*>(ii.instance);
if (!ret.ds.tracer) {
ds->TraceEpilogue(tls_state);
return ret;
}
FillIterator(&ii, ts, &ret);
ret.ds.tls = reinterpret_cast<struct PerfettoDsTlsImpl*>(tls_state);
return ret;
}
void PerfettoTeLlImplNext(struct PerfettoTeCategoryImpl* cat,
struct PerfettoTeTimestamp ts,
struct PerfettoTeLlImplIterator* iterator) {
auto* tls = reinterpret_cast<perfetto::internal::DataSourceThreadLocalState*>(
iterator->ds.tls);
perfetto::internal::DataSourceType::InstancesIterator ii;
ii.i = iterator->ds.inst_id;
ii.cached_instances = tls->root_tls->cached_instances;
ii.instance =
reinterpret_cast<perfetto::internal::DataSourceInstanceThreadLocalState*>(
iterator->ds.tracer);
perfetto::internal::DataSourceType* ds =
perfetto::shlib::TrackEvent::GetType();
ds->NextIteration</*Traits=*/perfetto::shlib::TracePointTraits>(&ii, tls,
{cat});
iterator->ds.inst_id = ii.i;
tls->root_tls->cached_instances = ii.cached_instances;
iterator->ds.tracer =
reinterpret_cast<struct PerfettoDsTracerImpl*>(ii.instance);
if (!iterator->ds.tracer) {
ds->TraceEpilogue(tls);
return;
}
FillIterator(&ii, ts, iterator);
}
void PerfettoTeLlImplBreak(struct PerfettoTeCategoryImpl*,
struct PerfettoTeLlImplIterator* iterator) {
auto* tls = reinterpret_cast<perfetto::internal::DataSourceThreadLocalState*>(
iterator->ds.tls);
perfetto::internal::DataSourceType* ds =
perfetto::shlib::TrackEvent::GetType();
ds->TraceEpilogue(tls);
}
bool PerfettoTeLlImplDynCatEnabled(
struct PerfettoDsTracerImpl* tracer,
PerfettoDsInstanceIndex inst_id,
const struct PerfettoTeCategoryDescriptor* dyn_cat) {
perfetto::internal::DataSourceType* ds =
perfetto::shlib::TrackEvent::GetType();
auto* tls_inst =
reinterpret_cast<perfetto::internal::DataSourceInstanceThreadLocalState*>(
tracer);
auto* incr_state = static_cast<perfetto::shlib::TrackEventIncrementalState*>(
ds->GetIncrementalState(tls_inst, inst_id));
return IsDynamicCategoryEnabled(inst_id, incr_state, *dyn_cat);
}
bool PerfettoTeLlImplTrackSeen(struct PerfettoTeLlImplIncr* incr,
uint64_t uuid) {
auto* incr_state =
reinterpret_cast<perfetto::shlib::TrackEventIncrementalState*>(incr);
return !incr_state->seen_track_uuids.insert(uuid).second;
}
uint64_t PerfettoTeLlImplIntern(struct PerfettoTeLlImplIncr* incr,
int32_t type,
const void* data,
size_t data_size,
bool* seen) {
auto* incr_state =
reinterpret_cast<perfetto::shlib::TrackEventIncrementalState*>(incr);
auto res = incr_state->iids.FindOrAssign(type, data, data_size);
*seen = !res.newly_assigned;
return res.iid;
}