Merge changes If73657de,I2602fe22
* changes:
Support perfetto::DynamicString in legacy macro
Support perfetto::DynamicString event name
diff --git a/docs/instrumentation/track-events.md b/docs/instrumentation/track-events.md
index 9837d6c..931a2a6 100644
--- a/docs/instrumentation/track-events.md
+++ b/docs/instrumentation/track-events.md
@@ -302,6 +302,51 @@
);
```
+## Dynamic event names
+
+Ideally all event name should be compile time string constants. For example:
+
+```C+++
+TRACE_EVENT_BEGIN("rendering", "DrawGame");
+```
+
+Here `"DrawGame"` is a compile time string. If we pass a dynamic string here,
+we will get compile time static_assert failure. For example :
+
+```C++
+const char* name = "DrawGame";
+TRACE_EVENT_BEGIN("rendering", name); // Error. Event name is not static.
+```
+
+There are two ways to use dynamic event name:
+
+1) If the event name is actually dynamic (e.g., std::string), write it using
+ `perfetto::DynamicName`:
+
+```C++
+ TRACE_EVENT("category", perfetto::DynamicName{dynamic_name});
+```
+
+Note: Below is the old way of using dynamic event names. It's not recommended
+ anymore.
+
+```C++
+TRACE_EVENT("category", nullptr, [&](perfetto::EventContext ctx) {
+ ctx.event()->set_name(dynamic_name);
+});
+```
+
+2) If the name is static, but the pointer is computed at runtime, wrap it
+ with perfetto::StaticString:
+
+```C++
+TRACE_EVENT("category", perfetto::StaticString{name});
+TRACE_EVENT("category", perfetto::StaticString{i % 2 == 0 ? "A" : "B"});
+```
+
+DANGER: Using perfetto::StaticString with strings whose contents change
+ dynamically can cause silent trace data corruption.
+
## Performance
Perfetto's trace points are designed to have minimal overhead when tracing is
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 757157f..4d51114 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -97,6 +97,29 @@
return true;
}
+// Taken from C++17
+template <typename...>
+using void_t = void;
+
+// Returns true iff `GetStaticString(T)` is defined OR T == DynamicString.
+template <typename T, typename = void>
+struct IsValidEventNameType
+ : std::is_same<perfetto::DynamicString, typename std::decay<T>::type> {};
+
+template <typename T>
+struct IsValidEventNameType<
+ T,
+ void_t<decltype(GetStaticString(std::declval<T>()))>> : std::true_type {};
+
+template <typename T>
+inline void ValidateEventNameType() {
+ static_assert(
+ IsValidEventNameType<T>::value,
+ "Event names must be static strings. To use dynamic event names, see "
+ "https://perfetto.dev/docs/instrumentation/"
+ "track-events#dynamic-event-names");
+}
+
} // namespace
// Traits for dynamic categories.
@@ -234,10 +257,12 @@
// - Zero or one lambda.
// Trace point which does not take a track or timestamp.
- template <typename CategoryType, typename... Arguments>
+ template <typename CategoryType,
+ typename EventNameType,
+ typename... Arguments>
static void TraceForCategory(uint32_t instances,
const CategoryType& category,
- const char* event_name,
+ const EventNameType& event_name,
perfetto::protos::pbzero::TrackEvent::Type type,
Arguments&&... args) PERFETTO_NO_INLINE {
TraceForCategoryImpl(instances, category, event_name, type,
@@ -253,12 +278,13 @@
// parsing track as a part of Arguments...).
template <typename TrackType,
typename CategoryType,
+ typename EventNameType,
typename... Arguments,
typename TrackTypeCheck = typename std::enable_if<
std::is_convertible<TrackType, Track>::value>::type>
static void TraceForCategory(uint32_t instances,
const CategoryType& category,
- const char* event_name,
+ const EventNameType& event_name,
perfetto::protos::pbzero::TrackEvent::Type type,
TrackType&& track,
Arguments&&... args) PERFETTO_NO_INLINE {
@@ -269,13 +295,14 @@
// Trace point which takes a timestamp, but not track.
template <typename CategoryType,
+ typename EventNameType,
typename TimestampType = uint64_t,
typename... Arguments,
typename TimestampTypeCheck = typename std::enable_if<
IsValidTimestamp<TimestampType>()>::type>
static void TraceForCategory(uint32_t instances,
const CategoryType& category,
- const char* event_name,
+ const EventNameType& event_name,
perfetto::protos::pbzero::TrackEvent::Type type,
TimestampType&& timestamp,
Arguments&&... args) PERFETTO_NO_INLINE {
@@ -288,6 +315,7 @@
// Trace point which takes a timestamp and a track.
template <typename TrackType,
typename CategoryType,
+ typename EventNameType,
typename TimestampType = uint64_t,
typename... Arguments,
typename TrackTypeCheck = typename std::enable_if<
@@ -296,7 +324,7 @@
IsValidTimestamp<TimestampType>()>::type>
static void TraceForCategory(uint32_t instances,
const CategoryType& category,
- const char* event_name,
+ const EventNameType& event_name,
perfetto::protos::pbzero::TrackEvent::Type type,
TrackType&& track,
TimestampType&& timestamp,
@@ -308,10 +336,10 @@
}
// Trace point with with a counter sample.
- template <typename CategoryType, typename ValueType>
+ template <typename CategoryType, typename EventNameType, typename ValueType>
static void TraceForCategory(uint32_t instances,
const CategoryType& category,
- const char*,
+ const EventNameType&,
perfetto::protos::pbzero::TrackEvent::Type type,
CounterTrack track,
ValueType value) PERFETTO_ALWAYS_INLINE {
@@ -322,13 +350,14 @@
// Trace point with with a timestamp and a counter sample.
template <typename CategoryType,
+ typename EventNameType,
typename TimestampType = uint64_t,
typename TimestampTypeCheck = typename std::enable_if<
IsValidTimestamp<TimestampType>()>::type,
typename ValueType>
static void TraceForCategory(uint32_t instances,
const CategoryType& category,
- const char*,
+ const EventNameType&,
perfetto::protos::pbzero::TrackEvent::Type type,
CounterTrack track,
TimestampType timestamp,
@@ -435,6 +464,7 @@
};
template <typename CategoryType,
+ typename EventNameType,
typename TrackType = Track,
typename TimestampType = uint64_t,
typename TimestampTypeCheck = typename std::enable_if<
@@ -445,7 +475,7 @@
static void TraceForCategoryImpl(
uint32_t instances,
const CategoryType& category,
- const char* event_name,
+ const EventNameType& event_name,
perfetto::protos::pbzero::TrackEvent::Type type,
const TrackType& track,
const TimestampType& timestamp,
@@ -483,8 +513,9 @@
bool on_current_thread_track =
(&track == &TrackEventInternal::kDefaultTrack);
auto event_ctx = TrackEventInternal::WriteEvent(
- trace_writer, incr_state, tls_state, static_category,
- event_name, type, trace_timestamp, on_current_thread_track);
+ trace_writer, incr_state, tls_state, static_category, type,
+ trace_timestamp, on_current_thread_track);
+ TrackEventInternal::WriteEventName(event_name, event_ctx, type);
// Write dynamic categories (except for events that don't require
// categories). For counter events, the counter name (and optional
// category) is stored as part of the track descriptor instead being
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index af67b7c..0e0bfe7 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -182,12 +182,31 @@
const protos::gen::TrackEventConfig& config,
const Category& category);
+ static void WriteEventName(const perfetto::DynamicString& event_name,
+ perfetto::EventContext& event_ctx,
+ perfetto::protos::pbzero::TrackEvent::Type);
+
+ static void WriteStaticEventName(const char* static_event_name,
+ perfetto::EventContext& event_ctx);
+
+ template <typename EventNameType,
+ typename Checker =
+ decltype(GetStaticString(std::declval<const EventNameType&>()))>
+ static void WriteEventName(const EventNameType& event_name,
+ perfetto::EventContext& event_ctx,
+ perfetto::protos::pbzero::TrackEvent::Type type) {
+ auto static_name = GetStaticString(event_name);
+ if (static_name != nullptr &&
+ type != protos::pbzero::TrackEvent::TYPE_SLICE_END) {
+ WriteStaticEventName(static_name, event_ctx);
+ }
+ }
+
static perfetto::EventContext WriteEvent(
TraceWriterBase*,
TrackEventIncrementalState*,
const TrackEventTlsState& tls_state,
const Category* category,
- const char* name,
perfetto::protos::pbzero::TrackEvent::Type,
const TraceTimestamp& timestamp,
bool on_current_thread_track);
diff --git a/include/perfetto/tracing/internal/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h
index da22586..a9a3c7a 100644
--- a/include/perfetto/tracing/internal/track_event_macros.h
+++ b/include/perfetto/tracing/internal/track_event_macros.h
@@ -23,6 +23,7 @@
#include "perfetto/base/compiler.h"
#include "perfetto/tracing/internal/track_event_data_source.h"
+#include "perfetto/tracing/string_helpers.h"
#include "perfetto/tracing/track_event_category_registry.h"
// Ignore GCC warning about a missing argument for a variadic macro parameter.
@@ -115,8 +116,9 @@
// Efficiently determines whether tracing is enabled for the given category, and
// if so, emits one trace event with the given arguments.
-#define PERFETTO_INTERNAL_TRACK_EVENT(category, ...) \
+#define PERFETTO_INTERNAL_TRACK_EVENT(category, name, ...) \
do { \
+ perfetto::internal::ValidateEventNameType<decltype(name)>(); \
namespace tns = ::PERFETTO_TRACK_EVENT_NAMESPACE; \
/* Compute the category index outside the lambda to work around a */ \
/* GCC 7 bug */ \
@@ -126,7 +128,7 @@
if (tns::internal::IsDynamicCategory(category)) { \
tns::TrackEvent::CallIfEnabled( \
[&](uint32_t instances) PERFETTO_NO_THREAD_SAFETY_ANALYSIS { \
- tns::TrackEvent::TraceForCategory(instances, category, \
+ tns::TrackEvent::TraceForCategory(instances, category, name, \
##__VA_ARGS__); \
}); \
} else { \
@@ -137,7 +139,7 @@
instances, \
PERFETTO_UID( \
kCatIndex_ADD_TO_PERFETTO_DEFINE_CATEGORIES_IF_FAILS_), \
- ##__VA_ARGS__); \
+ name, ##__VA_ARGS__); \
}); \
} \
} while (false)
diff --git a/include/perfetto/tracing/string_helpers.h b/include/perfetto/tracing/string_helpers.h
index c5d239f..7d0bd6b 100644
--- a/include/perfetto/tracing/string_helpers.h
+++ b/include/perfetto/tracing/string_helpers.h
@@ -40,34 +40,6 @@
const char* value;
};
-namespace internal {
-
-// Ensure that |string| is a static constant string.
-//
-// If you get a compiler failure here, you are most likely trying to use
-// TRACE_EVENT with a dynamic event name. There are two ways to fix this:
-//
-// 1) If the event name is actually dynamic (e.g., std::string), write it into
-// the event manually:
-//
-// TRACE_EVENT("category", nullptr, [&](perfetto::EventContext ctx) {
-// ctx.event()->set_name(dynamic_name);
-// });
-//
-// 2) If the name is static, but the pointer is computed at runtime, wrap it
-// with perfetto::StaticString:
-//
-// TRACE_EVENT("category", perfetto::StaticString{name});
-//
-// DANGER: Using perfetto::StaticString with strings whose contents change
-// dynamically can cause silent trace data corruption.
-//
-constexpr const char* GetStaticString(StaticString string) {
- return string.value;
-}
-
-} // namespace internal
-
// A explicit wrapper for marking strings as dynamic to ensure that perfetto
// doesn't try to cache the pointer value.
class PERFETTO_EXPORT_COMPONENT DynamicString {
@@ -81,6 +53,22 @@
size_t length;
};
+namespace internal {
+
+template <size_t N>
+constexpr const char* GetStaticString(const char (&string)[N]) {
+ return string;
+}
+
+constexpr std::nullptr_t GetStaticString(std::nullptr_t) {
+ return nullptr;
+}
+
+constexpr const char* GetStaticString(perfetto::StaticString string) {
+ return string.value;
+}
+
+} // namespace internal
} // namespace perfetto
#endif // INCLUDE_PERFETTO_TRACING_STRING_HELPERS_H_
diff --git a/include/perfetto/tracing/track_event.h b/include/perfetto/tracing/track_event.h
index 45499eb..c3e86d3 100644
--- a/include/perfetto/tracing/track_event.h
+++ b/include/perfetto/tracing/track_event.h
@@ -291,9 +291,9 @@
// TRACE_EVENT("category", "Name", perfetto::Track(1234),
// "arg", value, "arg2", value2);
//
-#define TRACE_EVENT_BEGIN(category, name, ...) \
- PERFETTO_INTERNAL_TRACK_EVENT( \
- category, ::perfetto::internal::GetStaticString(name), \
+#define TRACE_EVENT_BEGIN(category, name, ...) \
+ PERFETTO_INTERNAL_TRACK_EVENT( \
+ category, name, \
::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN, ##__VA_ARGS__)
// End a slice under |category|.
@@ -307,10 +307,10 @@
PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(category, name, ##__VA_ARGS__)
// Emit a slice which has zero duration.
-#define TRACE_EVENT_INSTANT(category, name, ...) \
- PERFETTO_INTERNAL_TRACK_EVENT( \
- category, ::perfetto::internal::GetStaticString(name), \
- ::perfetto::protos::pbzero::TrackEvent::TYPE_INSTANT, ##__VA_ARGS__)
+#define TRACE_EVENT_INSTANT(category, name, ...) \
+ PERFETTO_INTERNAL_TRACK_EVENT( \
+ category, name, ::perfetto::protos::pbzero::TrackEvent::TYPE_INSTANT, \
+ ##__VA_ARGS__)
// Efficiently determine if the given static or dynamic trace category or
// category group is enabled for tracing.
diff --git a/include/perfetto/tracing/track_event_legacy.h b/include/perfetto/tracing/track_event_legacy.h
index 0ec0ddd..f99d093 100644
--- a/include/perfetto/tracing/track_event_legacy.h
+++ b/include/perfetto/tracing/track_event_legacy.h
@@ -434,6 +434,23 @@
}
};
+// In legacy macro, `const char*` is considered static by default, unless it's
+// wrapped in `TRACE_STR_COPY`.
+// Given a `nullptr`, `const char (&)[N]`, `const char*` or `StaticString`,
+// convert it to `perfetto::StaticString`.
+template <typename T>
+inline ::perfetto::StaticString ConvertToStaticStringByDefault(const T& name);
+
+inline ::perfetto::DynamicString ConvertToStaticStringByDefault(
+ ::perfetto::DynamicString name) {
+ return name;
+}
+
+template <typename T>
+inline ::perfetto::StaticString ConvertToStaticStringByDefault(const T& name) {
+ return ::perfetto::StaticString{name};
+}
+
} // namespace internal
} // namespace perfetto
@@ -442,8 +459,7 @@
#define PERFETTO_INTERNAL_LEGACY_EVENT_ON_TRACK(phase, category, name, track, \
...) \
PERFETTO_INTERNAL_TRACK_EVENT( \
- category, \
- ::perfetto::internal::GetStaticString(::perfetto::StaticString{name}), \
+ category, ::perfetto::internal::ConvertToStaticStringByDefault(name), \
::perfetto::internal::TrackEventLegacy::PhaseToType(phase), track, \
##__VA_ARGS__);
@@ -503,12 +519,12 @@
// PERFETTO_INTERNAL_SCOPED_TRACK_EVENT does not require GetStaticString, as it
// uses TRACE_EVENT_BEGIN/END internally, which already have this call.
-#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...) \
- PERFETTO_INTERNAL_SCOPED_TRACK_EVENT( \
- category, ::perfetto::StaticString{name}, \
- [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS { \
- using ::perfetto::internal::TrackEventLegacy; \
- TrackEventLegacy::AddDebugAnnotations(&ctx, ##__VA_ARGS__); \
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...) \
+ PERFETTO_INTERNAL_SCOPED_TRACK_EVENT( \
+ category, ::perfetto::internal::ConvertToStaticStringByDefault(name), \
+ [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS { \
+ using ::perfetto::internal::TrackEventLegacy; \
+ TrackEventLegacy::AddDebugAnnotations(&ctx, ##__VA_ARGS__); \
})
// PERFETTO_INTERNAL_SCOPED_TRACK_EVENT does not require GetStaticString, as it
@@ -516,7 +532,7 @@
#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category, name, bind_id, \
flags, ...) \
PERFETTO_INTERNAL_SCOPED_TRACK_EVENT( \
- category, ::perfetto::StaticString{name}, \
+ category, ::perfetto::internal::ConvertToStaticStringByDefault(name), \
[&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS { \
using ::perfetto::internal::TrackEventLegacy; \
::perfetto::internal::LegacyTraceId PERFETTO_UID(trace_id){bind_id}; \
@@ -1230,7 +1246,7 @@
// involvement from the embedder. APIs such as TRACE_EVENT_API_ADD_TRACE_EVENT
// are still up to the embedder to define.
-#define TRACE_STR_COPY(str) (str)
+#define TRACE_STR_COPY(str) (perfetto::DynamicString{str})
#define TRACE_ID_WITH_SCOPE(scope, ...) \
::perfetto::internal::LegacyTraceId::WithScope(scope, ##__VA_ARGS__)
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index dbc87ab..fbcb023 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -438,12 +438,27 @@
}
// static
+void TrackEventInternal::WriteStaticEventName(
+ const char* static_event_name,
+ perfetto::EventContext& event_ctx) {
+ size_t name_iid = InternedEventName::Get(&event_ctx, static_event_name);
+ event_ctx.event()->set_name_iid(name_iid);
+}
+
+// static
+void TrackEventInternal::WriteEventName(
+ const perfetto::DynamicString& event_name,
+ perfetto::EventContext& event_ctx,
+ perfetto::protos::pbzero::TrackEvent::Type) {
+ event_ctx.event()->set_name(event_name.value, event_name.length);
+}
+
+// static
EventContext TrackEventInternal::WriteEvent(
TraceWriterBase* trace_writer,
TrackEventIncrementalState* incr_state,
const TrackEventTlsState& tls_state,
const Category* category,
- const char* name,
perfetto::protos::pbzero::TrackEvent::Type type,
const TraceTimestamp& timestamp,
bool on_current_thread_track) {
@@ -466,7 +481,7 @@
static_cast<int64_t>(tls_state.timestamp_unit_multiplier));
}
- // We assume that |category| and |name| point to strings with static lifetime.
+ // We assume that |category| points to the string with static lifetime.
// This means we can use their addresses as interning keys.
// TODO(skyostil): Intern categories at compile time.
if (category && type != protos::pbzero::TrackEvent::TYPE_SLICE_END &&
@@ -479,10 +494,6 @@
return true;
});
}
- if (name && type != protos::pbzero::TrackEvent::TYPE_SLICE_END) {
- size_t name_iid = InternedEventName::Get(&ctx, name);
- track_event->set_name_iid(name_iid);
- }
return ctx;
}
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 2293a73..0c4f52c 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -3226,6 +3226,23 @@
"B:test.Even", "B:test.Odd", "B:test.Even"));
}
+TEST_P(PerfettoApiTest, TrackEventEventNameDynamicString) {
+ // Create a new trace session.
+ auto* tracing_session = NewTraceWithCategories({"foo"});
+ tracing_session->get()->StartBlocking();
+ TRACE_EVENT_BEGIN("foo", perfetto::DynamicString{std::string("Event1")});
+ TRACE_EVENT_BEGIN("foo", perfetto::DynamicString{std::string("Event2")});
+ TRACE_EVENT0("foo", TRACE_STR_COPY(std::string("Event3")));
+ const char* event4 = "Event4";
+ TRACE_EVENT0("foo", event4);
+ auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
+ ASSERT_EQ(4u, slices.size());
+ EXPECT_EQ("B:foo.Event1", slices[0]);
+ EXPECT_EQ("B:foo.Event2", slices[1]);
+ EXPECT_EQ("B:foo.Event3", slices[2]);
+ EXPECT_EQ("B:foo.Event4", slices[3]);
+}
+
TEST_P(PerfettoApiTest, TrackEventArgumentsNotEvaluatedWhenDisabled) {
// Create a new trace session.
auto* tracing_session = NewTraceWithCategories({"foo"});