Shared library track event: API/ABI to emit data
This commit implements:
* New ABIs to emit events to the data source:
* A low level ABI that allows users to do their own message
serialization.
* A high level ABI that accepts a representation of the data to be
traced and performs serialization in the library.
* The PERFETTO_TE macro (similar to the TRACE_EVENT macro) that can be
used to emit trace events using the high level ABI.
Bug: 237053982
Change-Id: I0ca1a9b03dfa67f7618e27e176f3113eddca871a
diff --git a/src/shared_lib/BUILD.gn b/src/shared_lib/BUILD.gn
index 20adc15..0e3115b 100644
--- a/src/shared_lib/BUILD.gn
+++ b/src/shared_lib/BUILD.gn
@@ -12,8 +12,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import("../../gn/test.gni")
+
+source_set("intern_map") {
+ deps = [
+ "../../gn:default_deps",
+ "../../include/perfetto/public",
+ "../base",
+ ]
+ sources = [
+ "intern_map.cc",
+ "intern_map.h",
+ ]
+}
+
source_set("shared_lib") {
deps = [
+ ":intern_map",
"../../gn:default_deps",
"../../include/perfetto/protozero",
"../../include/perfetto/public",
@@ -29,6 +44,7 @@
"reset_for_testing.h",
"stream_writer.cc",
"stream_writer.h",
+ "thread_utils.cc",
"tracing_session.cc",
"track_event.cc",
]
@@ -42,3 +58,15 @@
]
public_deps = [ "../../include/perfetto/public" ]
}
+
+perfetto_unittest_source_set("unittests") {
+ testonly = true
+ deps = [
+ ":intern_map",
+ "../../gn:default_deps",
+ "../../gn:gtest_and_gmock",
+ "../base",
+ "../base:test_support",
+ ]
+ sources = [ "intern_map_unittest.cc" ]
+}
diff --git a/src/shared_lib/intern_map.cc b/src/shared_lib/intern_map.cc
new file mode 100644
index 0000000..a770287
--- /dev/null
+++ b/src/shared_lib/intern_map.cc
@@ -0,0 +1,38 @@
+/*
+ * 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 "src/shared_lib/intern_map.h"
+
+namespace perfetto {
+InternMap::InternMap() = default;
+
+InternMap::~InternMap() = default;
+
+InternMap::FindOrAssignRes InternMap::FindOrAssign(int32_t type,
+ const void* value,
+ size_t value_size) {
+ // This would be more efficient with heterogeneous lookups, but C++17 doesn't
+ // support that.
+ auto* it = map_.Find(Key::NonOwning(type, value, value_size));
+ if (it != nullptr) {
+ return {*it, false};
+ }
+ uint64_t iid = ++last_iid_by_type_[type];
+ it = map_.Insert(Key::Owning(type, value, value_size), iid).first;
+ return {iid, true};
+}
+
+} // namespace perfetto
diff --git a/src/shared_lib/intern_map.h b/src/shared_lib/intern_map.h
new file mode 100644
index 0000000..f5ac5b2
--- /dev/null
+++ b/src/shared_lib/intern_map.h
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_SHARED_LIB_INTERN_MAP_H_
+#define SRC_SHARED_LIB_INTERN_MAP_H_
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/public/fnv1a.h"
+
+namespace perfetto {
+
+// Assigns and maintains the mapping between "interned" data and iids (small
+// integers that can be used to refer to the same data without repeating it)
+// for different types.
+class InternMap {
+ public:
+ // Zero will never be assigned as a valid iid: it is used as the return value
+ // of Find() to signal failure.
+ using Iid = uint64_t;
+
+ InternMap();
+ ~InternMap();
+
+ // Given a value (identified by the memory buffer starting at `value`,
+ // `value_size` bytes long) of a specific `type`, finds if there was an
+ // existing iid associated with it, or assigns a new iid to it. Assigned iids
+ // are unique for a specific type, but are reused across different types.
+ struct FindOrAssignRes {
+ // Iid associated with the passed value.
+ Iid iid;
+ // Whether the iid was newly assigned in this call (i.e. true if the value
+ // was not seen before).
+ bool newly_assigned;
+ };
+ FindOrAssignRes FindOrAssign(int32_t type,
+ const void* value,
+ size_t value_size);
+
+ private:
+ // Stores a value of a specific type. If the value is small, it is stored
+ // inline, otherwise it is stored in an external buffer. The Key can own the
+ // external buffer (when the key is stored in the map) or not (when the key is
+ // just used for lookup).
+ class Key {
+ public:
+ static Key NonOwning(int32_t type, const void* value, size_t value_size) {
+ Key key;
+ key.type_ = type;
+ key.val_type_ = ValueStorage::kNonOwnedPtr;
+ key.ptr_ = value;
+ key.value_size_ = value_size;
+ return key;
+ }
+ static Key Owning(int32_t type, const void* value, size_t value_size) {
+ Key key;
+ key.type_ = type;
+ key.value_size_ = value_size;
+ if (value_size < sizeof(value_buf_)) {
+ key.val_type_ = ValueStorage::kInline;
+ memcpy(key.value_buf_, value, value_size);
+ } else {
+ key.val_type_ = ValueStorage::kOwnedPtr;
+ char* newvalue = new char[value_size];
+ memcpy(newvalue, value, value_size);
+ key.owned_ptr_ = newvalue;
+ }
+ return key;
+ }
+ ~Key() {
+ if (val_type_ == ValueStorage::kOwnedPtr) {
+ delete[] owned_ptr_;
+ }
+ }
+ Key(const Key&) = delete;
+ Key(Key&& other) noexcept {
+ type_ = other.type_;
+ value_size_ = other.value_size_;
+ switch (other.val_type_) {
+ case ValueStorage::kNonOwnedPtr:
+ val_type_ = ValueStorage::kNonOwnedPtr;
+ ptr_ = other.ptr_;
+ return;
+ case ValueStorage::kOwnedPtr:
+ val_type_ = ValueStorage::kOwnedPtr;
+ owned_ptr_ = other.owned_ptr_;
+ other.val_type_ = ValueStorage::kInline;
+ other.value_size_ = 0;
+ return;
+ case ValueStorage::kInline:
+ val_type_ = ValueStorage::kInline;
+ memcpy(value_buf_, other.value_buf_, value_size_);
+ return;
+ }
+ }
+ bool operator==(const Key& other) const {
+ if (type_ != other.type_) {
+ return false;
+ }
+ if (value_size_ != other.value_size_) {
+ return false;
+ }
+ return memcmp(value(), other.value(), value_size_) == 0;
+ }
+ const void* value() const {
+ switch (val_type_) {
+ case ValueStorage::kNonOwnedPtr:
+ return ptr_;
+ case ValueStorage::kOwnedPtr:
+ return owned_ptr_;
+ case ValueStorage::kInline:
+ break;
+ }
+ return &value_buf_;
+ }
+ struct Hash {
+ size_t operator()(const Key& obj) const {
+ return std::hash<int32_t>()(obj.type_) ^
+ static_cast<size_t>(PerfettoFnv1a(obj.value(), obj.value_size_));
+ }
+ };
+
+ private:
+ enum class ValueStorage {
+ kInline, // `value_buf_` is used directly to store the value.
+ kNonOwnedPtr, // `ptr_` points to an external buffer that stores the
+ // value. Not owned by this Key.
+ kOwnedPtr, // `owned_ptr_` points to an external buffer that stores
+ // the value. Owned by this Key.
+ };
+ Key() = default;
+ int32_t type_;
+ ValueStorage val_type_;
+ size_t value_size_;
+ union {
+ char value_buf_[sizeof(uint64_t)];
+ const void* ptr_;
+ char* owned_ptr_;
+ };
+ };
+
+ base::FlatHashMap<Key, uint64_t, Key::Hash> map_;
+ base::FlatHashMap<int32_t, uint64_t> last_iid_by_type_;
+};
+
+} // namespace perfetto
+
+#endif // SRC_SHARED_LIB_INTERN_MAP_H_
diff --git a/src/shared_lib/intern_map_unittest.cc b/src/shared_lib/intern_map_unittest.cc
new file mode 100644
index 0000000..837c960
--- /dev/null
+++ b/src/shared_lib/intern_map_unittest.cc
@@ -0,0 +1,86 @@
+/*
+ * 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 "src/shared_lib/intern_map.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace {
+
+TEST(InternMapTest, SmallValue) {
+ const char kSmallValue[] = "A";
+ InternMap iids;
+
+ auto res1 = iids.FindOrAssign(/*type=*/0, kSmallValue, sizeof(kSmallValue));
+
+ EXPECT_TRUE(res1.newly_assigned);
+ EXPECT_NE(res1.iid, 0u);
+
+ auto res2 = iids.FindOrAssign(/*type=*/0, kSmallValue, sizeof(kSmallValue));
+
+ EXPECT_FALSE(res2.newly_assigned);
+ EXPECT_EQ(res1.iid, res1.iid);
+}
+
+TEST(InternMapTest, BigValue) {
+ const char kBigValue[] = "ABCDEFGHIJKLMNOP";
+ InternMap iids;
+
+ auto res1 = iids.FindOrAssign(/*type=*/0, kBigValue, sizeof(kBigValue));
+
+ EXPECT_TRUE(res1.newly_assigned);
+ EXPECT_NE(res1.iid, 0u);
+
+ auto res2 = iids.FindOrAssign(/*type=*/0, kBigValue, sizeof(kBigValue));
+
+ EXPECT_FALSE(res2.newly_assigned);
+ EXPECT_EQ(res1.iid, res1.iid);
+}
+
+TEST(InternMapTest, TwoValuesSameType) {
+ const char kValue1[] = "A";
+ const char kValue2[] = "ABCDEFGHIJKLMNOP";
+ InternMap iids;
+
+ auto res1 = iids.FindOrAssign(/*type=*/0, kValue1, sizeof(kValue1));
+
+ EXPECT_TRUE(res1.newly_assigned);
+ EXPECT_NE(res1.iid, 0u);
+
+ auto res2 = iids.FindOrAssign(/*type=*/0, kValue2, sizeof(kValue2));
+
+ EXPECT_TRUE(res1.newly_assigned);
+ EXPECT_NE(res1.iid, res2.iid);
+}
+
+TEST(InternMapTest, SameValueDifferentTypes) {
+ const char kValue[] = "A";
+ InternMap iids;
+
+ auto res1 = iids.FindOrAssign(/*type=*/0, kValue, sizeof(kValue));
+
+ EXPECT_TRUE(res1.newly_assigned);
+ EXPECT_NE(res1.iid, 0u);
+
+ auto res2 = iids.FindOrAssign(/*type=*/1, kValue, sizeof(kValue));
+
+ EXPECT_TRUE(res1.newly_assigned);
+ EXPECT_NE(res2.iid, 0u);
+}
+
+} // namespace
+} // namespace perfetto
diff --git a/src/shared_lib/thread_utils.cc b/src/shared_lib/thread_utils.cc
new file mode 100644
index 0000000..372b832
--- /dev/null
+++ b/src/shared_lib/thread_utils.cc
@@ -0,0 +1,23 @@
+/*
+ * 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/thread_utils_abi.h"
+
+#include "perfetto/base/thread_utils.h"
+
+PerfettoThreadId PerfettoGetThreadIdImpl(void) {
+ return perfetto::base::GetThreadId();
+}
diff --git a/src/shared_lib/track_event.cc b/src/shared_lib/track_event.cc
index 42511cf..01be391 100644
--- a/src/shared_lib/track_event.cc
+++ b/src/shared_lib/track_event.cc
@@ -15,6 +15,8 @@
*/
#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>
@@ -25,6 +27,7 @@
#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"
@@ -47,12 +50,15 @@
#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};
@@ -192,8 +198,60 @@
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, DefaultDataSourceTraits> {
+ : public perfetto::DataSource<TrackEvent, TrackEventDataSourceTraits> {
public:
~TrackEvent() override;
void OnSetup(const DataSourceBase::SetupArgs& args) override {
@@ -365,9 +423,13 @@
} // namespace shlib
} // namespace perfetto
-PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(perfetto::shlib::TrackEvent);
+PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(
+ perfetto::shlib::TrackEvent,
+ perfetto::shlib::TrackEventDataSourceTraits);
-PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(perfetto::shlib::TrackEvent);
+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();
@@ -378,6 +440,332 @@
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));
+ }
+}
+
+void WriteTrackEvent(perfetto::shlib::TrackEventIncrementalState* incr,
+ perfetto::protos::pbzero::TrackEvent* event,
+ PerfettoTeCategoryImpl* cat,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ const char* name,
+ const PerfettoTeHlExtra* extra_data,
+ std::optional<uint64_t> track_uuid,
+ const PerfettoTeCategoryDescriptor* dynamic_cat) {
+ 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) {
+ 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; it = it->next) {
+ if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_INT64 &&
+ type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER) {
+ event->set_counter_value(
+ reinterpret_cast<const struct PerfettoTeHlExtraCounterInt64*>(it)
+ ->value);
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_DOUBLE) {
+ event->set_double_counter_value(
+ reinterpret_cast<const struct PerfettoTeHlExtraCounterDouble*>(it)
+ ->value);
+ }
+ }
+
+ for (const auto* it = extra_data; it; it = it->next) {
+ if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_BOOL ||
+ it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_UINT64 ||
+ it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_INT64 ||
+ it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_DOUBLE ||
+ it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_STRING ||
+ it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_POINTER) {
+ auto* dbg = event->add_debug_annotations();
+ const char* arg_name = nullptr;
+ if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_BOOL) {
+ auto* arg =
+ reinterpret_cast<const struct PerfettoTeHlExtraDebugArgBool*>(it);
+ dbg->set_bool_value(arg->value);
+ arg_name = arg->name;
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_UINT64) {
+ auto* arg =
+ reinterpret_cast<const struct PerfettoTeHlExtraDebugArgUint64*>(it);
+ dbg->set_uint_value(arg->value);
+ arg_name = arg->name;
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_INT64) {
+ auto* arg =
+ reinterpret_cast<const struct PerfettoTeHlExtraDebugArgInt64*>(it);
+ dbg->set_int_value(arg->value);
+ arg_name = arg->name;
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_DOUBLE) {
+ auto* arg =
+ reinterpret_cast<const struct PerfettoTeHlExtraDebugArgDouble*>(it);
+ dbg->set_double_value(arg->value);
+ arg_name = arg->name;
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_STRING) {
+ auto* arg =
+ reinterpret_cast<const struct PerfettoTeHlExtraDebugArgString*>(it);
+ dbg->set_string_value(arg->value);
+ arg_name = arg->name;
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_POINTER) {
+ auto* arg =
+ reinterpret_cast<const struct PerfettoTeHlExtraDebugArgPointer*>(
+ it);
+ 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; it = it->next) {
+ if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_FLOW) {
+ event->add_flow_ids(
+ reinterpret_cast<const struct PerfettoTeHlExtraFlow*>(it)->id);
+ }
+ }
+
+ for (const auto* it = extra_data; it; it = it->next) {
+ if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_TERMINATING_FLOW) {
+ event->add_terminating_flow_ids(
+ reinterpret_cast<const struct PerfettoTeHlExtraFlow*>(it)->id);
+ }
+ }
+}
+
+} // namespace
+
struct PerfettoTeCategoryImpl* PerfettoTeCategoryImplCreate(
struct PerfettoTeCategoryDescriptor* desc) {
auto* cat = new PerfettoTeCategoryImpl;
@@ -412,4 +800,366 @@
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;
+}
+
+static void AdvanceToFirstEnabledDynamicCategory(
+ perfetto::internal::DataSourceType::InstancesIterator* ii,
+ perfetto::internal::DataSourceThreadLocalState* tls_state,
+ perfetto::shlib::TrackEventIncrementalState* incr_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})) {
+ 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,
+ const struct PerfettoTeHlExtra* 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;
+
+ for (const auto* it = extra_data; it; it = it->next) {
+ if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_REGISTERED_TRACK) {
+ auto* cast =
+ reinterpret_cast<const struct PerfettoTeHlExtraRegisteredTrack*>(it);
+ registered_track = cast->track;
+ named_track = nullptr;
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_NAMED_TRACK) {
+ registered_track = nullptr;
+ named_track =
+ reinterpret_cast<const struct PerfettoTeHlExtraNamedTrack*>(it);
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_TIMESTAMP) {
+ custom_timestamp =
+ reinterpret_cast<const struct PerfettoTeHlExtraTimestamp*>(it);
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_DYNAMIC_CATEGORY) {
+ dynamic_cat =
+ reinterpret_cast<const struct PerfettoTeHlExtraDynamicCategory*>(it)
+ ->desc;
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_INT64) {
+ int_counter =
+ reinterpret_cast<const struct PerfettoTeHlExtraCounterInt64*>(it)
+ ->value;
+ } else if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_DOUBLE) {
+ double_counter =
+ reinterpret_cast<const struct PerfettoTeHlExtraCounterInt64*>(it)
+ ->value;
+ }
+ }
+
+ perfetto::TraceTimestamp ts;
+ if (custom_timestamp) {
+ ts.clock_id = custom_timestamp->timestamp.clock_id;
+ ts.value = custom_timestamp->timestamp.value;
+ } else {
+ ts = TrackEventInternal::GetTraceTime();
+ }
+
+ 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 (PERFETTO_UNLIKELY(dynamic_cat)) {
+ AdvanceToFirstEnabledDynamicCategory(ii, tls_state, incr_state, cat,
+ *dynamic_cat);
+ if (!ii->instance) {
+ return;
+ }
+ }
+
+ 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;
+ }
+
+ {
+ auto packet = NewTracePacketInternal(
+ ii->instance->trace_writer.get(), 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);
+ 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();
+ }
+ }
+
+ bool flush = false;
+ for (const auto* it = extra_data; it; it = it->next) {
+ if (it->type == PERFETTO_TE_HL_EXTRA_TYPE_FLUSH) {
+ flush = true;
+ }
+ }
+ if (PERFETTO_UNLIKELY(flush)) {
+ ii->instance->trace_writer->Flush();
+ }
+}
+
+void PerfettoTeHlEmitImpl(struct PerfettoTeCategoryImpl* cat,
+ int32_t type,
+ const char* name,
+ const struct PerfettoTeHlExtra* 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;
}
diff --git a/src/tracing/track.cc b/src/tracing/track.cc
index a43cc84..2f57e77 100644
--- a/src/tracing/track.cc
+++ b/src/tracing/track.cc
@@ -181,7 +181,11 @@
if (instance_)
return;
instance_ = new TrackRegistry();
+ Track::process_uuid = ComputeProcessUuid();
+}
+// static
+uint64_t TrackRegistry::ComputeProcessUuid() {
// Use the process start time + pid as the unique identifier for this process.
// This ensures that if there are two independent copies of the Perfetto SDK
// in the same process (e.g., one in the app and another in a system
@@ -191,11 +195,11 @@
base::Hasher hash;
hash.Update(start_time);
hash.Update(Platform::GetCurrentProcessId());
- Track::process_uuid = hash.digest();
- } else {
- // Fall back to a randomly generated identifier.
- Track::process_uuid = static_cast<uint64_t>(base::Uuidv4().lsb());
+ return hash.digest();
}
+ // Fall back to a randomly generated identifier.
+ static uint64_t random_once = static_cast<uint64_t>(base::Uuidv4().lsb());
+ return random_once;
}
void TrackRegistry::ResetForTesting() {