blob: 889c41113230f4030615a5e6b0a35e6a4dbf312a [file] [edit]
/*
* Copyright (C) 2025 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_hl_abi.h"
#include "perfetto/tracing/internal/track_event_internal.h"
#include "src/shared_lib/track_event/ds.h"
#include "src/shared_lib/track_event/serialization.h"
namespace perfetto::shlib {
namespace {
using perfetto::internal::TrackEventInternal;
// All interned string messages for track events must have this field number
// structure.
static constexpr uint32_t kInternedStringIidFieldNumber = 1;
static constexpr uint32_t kInternedStringNameFieldNumber = 2;
protos::pbzero::TrackEvent::Type EventType(int32_t type) {
using Type = 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;
}
// Appends the fields described by `fields` to `msg`.
void AppendHlProtoFields(TrackEventIncrementalState* incr,
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_CSTR_INTERNED: {
auto field = reinterpret_cast<PerfettoTeHlProtoFieldCstrInterned*>(*p);
PERFETTO_DCHECK(field->interned_type_id != 0);
if (field->interned_type_id) {
const char* str = field->str;
size_t len = strlen(field->str);
auto res = incr->iids.FindOrAssign(
static_cast<int32_t>(field->interned_type_id), str, len);
if (res.newly_assigned) {
auto* ser = incr->serialized_interned_data
->BeginNestedMessage<protozero::Message>(
field->interned_type_id);
ser->AppendVarInt(kInternedStringIidFieldNumber, res.iid);
ser->AppendString(kInternedStringNameFieldNumber, field->str);
}
msg->AppendVarInt(field->header.id, res.iid);
}
// If interned_type_id is zero, this is a user error, we drop the packet
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(incr, 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(TrackEventIncrementalState* incr,
protos::pbzero::TrackEvent* event,
PerfettoTeCategoryImpl* cat,
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 != protos::pbzero::TrackEvent::TYPE_UNSPECIFIED) {
event->set_type(type);
}
if (!dynamic_cat && type != protos::pbzero::TrackEvent::TYPE_SLICE_END &&
type != protos::pbzero::TrackEvent::TYPE_COUNTER) {
uint64_t iid = cat->cat_iid;
auto res = incr->iids.FindOrAssign(
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 != 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(
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 != protos::pbzero::TrackEvent::TYPE_SLICE_END &&
type != 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 == 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(
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(incr, event, fields);
}
}
}
uint64_t EmitNamedTrack(uint64_t parent_uuid,
const char* name,
uint64_t id,
bool is_name_static,
perfetto::shlib::TrackEventIncrementalState* incr_state,
perfetto::TraceWriterBase* trace_writer) {
uint64_t uuid = parent_uuid;
uuid ^= PerfettoFnv1a(name, strlen(name));
uuid ^= id;
if (incr_state->seen_track_uuids.insert(uuid).second) {
auto packet = trace_writer->NewTracePacket();
auto* track_descriptor = packet->set_track_descriptor();
track_descriptor->set_uuid(uuid);
if (parent_uuid) {
track_descriptor->set_parent_uuid(parent_uuid);
}
if (is_name_static) {
track_descriptor->set_static_name(name);
} else {
track_descriptor->set_name(name);
}
}
return uuid;
}
uint64_t EmitRegisteredTrack(
const PerfettoTeRegisteredTrackImpl* registered_track,
perfetto::shlib::TrackEventIncrementalState* incr_state,
perfetto::TraceWriterBase* trace_writer) {
if (incr_state->seen_track_uuids.insert(registered_track->uuid).second) {
auto packet = trace_writer->NewTracePacket();
auto* track_descriptor = packet->set_track_descriptor();
track_descriptor->AppendRawProtoBytes(registered_track->descriptor,
registered_track->descriptor_size);
}
return registered_track->uuid;
}
uint64_t EmitProtoTrack(uint64_t uuid,
PerfettoTeHlProtoField* const* fields,
perfetto::shlib::TrackEventIncrementalState* incr_state,
perfetto::TraceWriterBase* trace_writer) {
if (incr_state->seen_track_uuids.insert(uuid).second) {
auto packet = trace_writer->NewTracePacket();
auto* track_descriptor = packet->set_track_descriptor();
track_descriptor->set_uuid(uuid);
AppendHlProtoFields(incr_state, track_descriptor, fields);
}
return uuid;
}
uint64_t EmitProtoTrackWithParentUuid(
uint64_t uuid,
uint64_t parent_uuid,
PerfettoTeHlProtoField* const* fields,
perfetto::shlib::TrackEventIncrementalState* incr_state,
perfetto::TraceWriterBase* trace_writer) {
if (incr_state->seen_track_uuids.insert(uuid).second) {
auto packet = trace_writer->NewTracePacket();
auto* track_descriptor = packet->set_track_descriptor();
track_descriptor->set_uuid(uuid);
track_descriptor->set_parent_uuid(parent_uuid);
AppendHlProtoFields(incr_state, track_descriptor, fields);
}
return uuid;
}
// 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.
void AdvanceToFirstEnabledDynamicCategory(
internal::DataSourceType::InstancesIterator* ii,
internal::DataSourceThreadLocalState* tls_state,
struct PerfettoTeCategoryImpl* cat,
const PerfettoTeCategoryDescriptor& dyn_cat) {
internal::DataSourceType* ds = shlib::TrackEvent::GetType();
for (; ii->instance; ds->NextIteration</*Traits=*/shlib::TracePointTraits>(
ii, tls_state, {cat})) {
auto* incr_state = static_cast<TrackEventIncrementalState*>(
ds->GetIncrementalState(ii->instance, ii->i));
if (shlib::TrackEvent::IsDynamicCategoryEnabled(ii->i, incr_state,
dyn_cat)) {
break;
}
}
}
void InstanceOp(internal::DataSourceType* ds,
internal::DataSourceType::InstancesIterator* ii,
internal::DataSourceThreadLocalState* tls_state,
struct PerfettoTeCategoryImpl* cat,
protos::pbzero::TrackEvent::Type type,
const char* name,
struct PerfettoTeHlExtra* const* extra_data) {
if (!ii->instance) {
return;
}
std::variant<std::monostate, const PerfettoTeRegisteredTrackImpl*,
const PerfettoTeHlExtraNamedTrack*,
const PerfettoTeHlExtraProtoTrack*,
const PerfettoTeHlExtraNestedTracks*>
track;
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);
track = cast.track;
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_NAMED_TRACK) {
track =
&reinterpret_cast<const struct PerfettoTeHlExtraNamedTrack&>(extra);
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_PROTO_TRACK) {
track =
&reinterpret_cast<const struct PerfettoTeHlExtraProtoTrack&>(extra);
} else if (extra.type == PERFETTO_TE_HL_EXTRA_TYPE_NESTED_TRACKS) {
auto* nested =
&reinterpret_cast<const struct PerfettoTeHlExtraNestedTracks&>(extra);
track = nested;
} 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 PerfettoTeHlExtraCounterDouble&>(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;
}
}
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;
}
}
perfetto::TraceWriterBase* trace_writer = ii->instance->trace_writer.get();
const auto& track_event_tls = *static_cast<TrackEventTlsState*>(
ii->instance->data_source_custom_tls.get());
auto* incr_state = static_cast<TrackEventIncrementalState*>(
ds->GetIncrementalState(ii->instance, ii->i));
ResetIncrementalStateIfRequired(trace_writer, incr_state, track_event_tls,
ts);
if (std::holds_alternative<const PerfettoTeRegisteredTrackImpl*>(track)) {
auto* registered_track =
std::get<const PerfettoTeRegisteredTrackImpl*>(track);
track_uuid =
EmitRegisteredTrack(registered_track, incr_state, trace_writer);
} else if (std::holds_alternative<const PerfettoTeHlExtraNamedTrack*>(
track)) {
auto* named_track = std::get<const PerfettoTeHlExtraNamedTrack*>(track);
track_uuid = EmitNamedTrack(named_track->parent_uuid, named_track->name,
named_track->id, named_track->is_name_static,
incr_state, trace_writer);
} else if (std::holds_alternative<const PerfettoTeHlExtraProtoTrack*>(
track)) {
auto* proto_track = std::get<const PerfettoTeHlExtraProtoTrack*>(track);
track_uuid = EmitProtoTrack(proto_track->uuid, proto_track->fields,
incr_state, trace_writer);
} else if (std::holds_alternative<const PerfettoTeHlExtraNestedTracks*>(
track)) {
auto* nested = std::get<const PerfettoTeHlExtraNestedTracks*>(track);
uint64_t uuid = 0;
for (PerfettoTeHlNestedTrack* const* tp = nested->tracks; *tp != nullptr;
tp++) {
auto track_type =
static_cast<enum PerfettoTeHlNestedTrackType>((*tp)->type);
switch (track_type) {
case PERFETTO_TE_HL_NESTED_TRACK_TYPE_NAMED: {
auto* named_track =
reinterpret_cast<PerfettoTeHlNestedTrackNamed*>(*tp);
// Currently static names for nested tracks is not supported.
uuid = EmitNamedTrack(uuid, named_track->name, named_track->id,
/*is_name_static_=*/false, incr_state,
trace_writer);
} break;
case PERFETTO_TE_HL_NESTED_TRACK_TYPE_PROCESS: {
uuid = perfetto_te_process_track_uuid;
} break;
case PERFETTO_TE_HL_NESTED_TRACK_TYPE_THREAD: {
uuid = perfetto_te_process_track_uuid ^
static_cast<uint64_t>(perfetto::base::GetThreadId());
} break;
case PERFETTO_TE_HL_NESTED_TRACK_TYPE_PROTO: {
auto* proto_track =
reinterpret_cast<PerfettoTeHlNestedTrackProto*>(*tp);
uuid = EmitProtoTrackWithParentUuid(proto_track->id ^ uuid, uuid,
proto_track->fields, incr_state,
trace_writer);
} break;
case PERFETTO_TE_HL_NESTED_TRACK_TYPE_REGISTERED: {
auto* registered_track =
reinterpret_cast<PerfettoTeHlNestedTrackRegistered*>(*tp);
uuid = EmitRegisteredTrack(registered_track->track, incr_state,
trace_writer);
} break;
}
}
track_uuid = uuid;
}
{
auto packet = NewTracePacketInternal(
trace_writer, incr_state, track_event_tls, ts,
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(
protos::pbzero::TracePacket::kInternedDataFieldNumber, ranges.data(),
ranges.size());
incr_state->serialized_interned_data.Reset();
}
}
if (PERFETTO_UNLIKELY(flush)) {
trace_writer->Flush();
}
}
void TeHlEmit(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})) {
perfetto::shlib::InstanceOp(ds, &ii, tls_state, cat,
perfetto::shlib::EventType(type), name,
extra_data);
}
ds->TraceEpilogue(tls_state);
}
} // namespace
} // namespace perfetto::shlib
void PerfettoTeHlEmitImpl(struct PerfettoTeCategoryImpl* cat,
int32_t type,
const char* name,
struct PerfettoTeHlExtra* const* extra_data) {
perfetto::shlib::TeHlEmit(cat, type, name, extra_data);
}