[tracing] Fix combining untyped and typed args in TRACE_EVENT.
Fix the compilation of passing a typed argument to TRACE_EVENT macro
after and untyped one (i.e. TRACE_EVENT(..., "arg", "value",
TrackEvent::kFoo, foo);) discovered in crrev.com/c/3178983.
This patch refactores WriteTrackEventArgs to fix that (using a
forward-declaration of untyped version) and adds integration tests
covering both untyped-after-typed and typed-after-untyped scenarios.
R=eseckler@google.com
CC=hbolaria@google.com
Change-Id: I61d3a9da31f93af6c6e01f4e76e812682cbed59e
diff --git a/include/perfetto/tracing/internal/write_track_event_args.h b/include/perfetto/tracing/internal/write_track_event_args.h
index 2f84878..ab215ae 100644
--- a/include/perfetto/tracing/internal/write_track_event_args.h
+++ b/include/perfetto/tracing/internal/write_track_event_args.h
@@ -24,19 +24,7 @@
namespace perfetto {
namespace internal {
-// Helper function handling filling provided |EventContext| from the provided
-// arguments, which include:
-// - Lambda functions,
-// - Debug annotations.
-//
-// TRACE_EVENT parameters which do not translate to directly writing something
-// into TrackEvent proto (like tracks and timestamps are _not_ covered by this
-// function).
-template <typename... Args, typename TypeCheck = void>
-void WriteTrackEventArgs(EventContext event_context, Args&&... args);
-
// No arguments means that we don't have to write anything.
-template <>
PERFETTO_ALWAYS_INLINE inline void WriteTrackEventArgs(EventContext) {}
namespace {
@@ -80,30 +68,25 @@
arg_function(std::move(event_ctx));
}
-// Write one debug annotation and recursively write the rest of the arguments.
+// Forward-declare the specification for writing untyped arguments to ensure
+// that typed specification could recursively pick it up.
template <typename ArgValue, typename... Args>
PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(EventContext event_ctx,
const char* arg_name,
ArgValue&& arg_value,
- Args&&... args) {
- TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
- std::forward<ArgValue>(arg_value));
- WriteTrackEventArgs(std::move(event_ctx), std::forward<Args>(args)...);
-}
+ Args&&... args);
// Write one typed message and recursively write the rest of the arguments.
-template <typename FieldMetadataType,
- typename ArgValue,
- typename... Args,
- typename Check = base::enable_if_t<
- std::is_base_of<protozero::proto_utils::FieldMetadataBase,
- FieldMetadataType>::value>>
+template <typename FieldMetadataType, typename ArgValue, typename... Args>
PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(
EventContext event_ctx,
protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadataType>
field_name,
ArgValue&& arg_value,
Args&&... args) {
+ static_assert(std::is_base_of<protozero::proto_utils::FieldMetadataBase,
+ FieldMetadataType>::value,
+ "");
static_assert(
std::is_base_of<protos::pbzero::TrackEvent,
typename FieldMetadataType::message_type>::value,
@@ -116,6 +99,17 @@
WriteTrackEventArgs(std::move(event_ctx), std::forward<Args>(args)...);
}
+// Write one debug annotation and recursively write the rest of the arguments.
+template <typename ArgValue, typename... Args>
+PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(EventContext event_ctx,
+ const char* arg_name,
+ ArgValue&& arg_value,
+ Args&&... args) {
+ TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
+ std::forward<ArgValue>(arg_value));
+ WriteTrackEventArgs(std::move(event_ctx), std::forward<Args>(args)...);
+}
+
} // namespace internal
} // namespace perfetto
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index ed08a17..5e93578 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -498,7 +498,11 @@
std::vector<std::string> ReadSlicesFromTrace(
perfetto::TracingSession* tracing_session) {
- std::vector<char> raw_trace = tracing_session->ReadTraceBlocking();
+ return ReadSlicesFromTrace(tracing_session->ReadTraceBlocking());
+ }
+
+ std::vector<std::string> ReadSlicesFromTrace(
+ const std::vector<char>& raw_trace) {
EXPECT_GE(raw_trace.size(), 0u);
// Read back the trace, maintaining interning tables as we go.
@@ -1837,16 +1841,20 @@
EXPECT_TRUE(found_args);
}
-TEST_P(PerfettoApiTest, InlineTrackEventTypedArgs_NestedSingle) {
- struct LogMessage {
- void WriteIntoTrace(
- perfetto::TracedProto<perfetto::protos::pbzero::LogMessage> context)
- const {
- context->set_source_location_iid(1);
- context->set_body_iid(2);
- }
- };
+namespace {
+struct LogMessage {
+ void WriteIntoTrace(
+ perfetto::TracedProto<perfetto::protos::pbzero::LogMessage> context)
+ const {
+ context->set_source_location_iid(1);
+ context->set_body_iid(2);
+ }
+};
+
+} // namespace
+
+TEST_P(PerfettoApiTest, InlineTrackEventTypedArgs_NestedSingle) {
// Create a new trace session.
auto* tracing_session = NewTraceWithCategories({"foo"});
tracing_session->get()->StartBlocking();
@@ -1882,6 +1890,90 @@
EXPECT_TRUE(found_args);
}
+TEST_P(PerfettoApiTest, InlineTrackEventTypedAndUntypedArgs) {
+ // Create a new trace session.
+ auto* tracing_session = NewTraceWithCategories({"foo"});
+ tracing_session->get()->StartBlocking();
+
+ TRACE_EVENT_BEGIN("foo", "E",
+ perfetto::protos::pbzero::TrackEvent::kLogMessage,
+ LogMessage(), "arg", "value");
+ TRACE_EVENT_END("foo");
+
+ tracing_session->get()->StopBlocking();
+
+ std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+ std::string trace(raw_trace.data(), raw_trace.size());
+
+ perfetto::protos::gen::Trace parsed_trace;
+ ASSERT_TRUE(parsed_trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+ // Find typed argument.
+ bool found_args = false;
+ for (const auto& packet : parsed_trace.packet()) {
+ if (!packet.has_track_event())
+ continue;
+ const auto& track_event = packet.track_event();
+ if (track_event.type() !=
+ perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN) {
+ continue;
+ }
+
+ EXPECT_TRUE(track_event.has_log_message());
+ const auto& log = track_event.log_message();
+ EXPECT_EQ(1u, log.source_location_iid());
+ EXPECT_EQ(2u, log.body_iid());
+ found_args = true;
+ }
+ EXPECT_TRUE(found_args);
+
+ // Find untyped argument.
+ EXPECT_THAT(ReadSlicesFromTrace(raw_trace),
+ ElementsAre("B:foo.E(arg=(string)value)"));
+}
+
+TEST_P(PerfettoApiTest, InlineTrackEventUntypedAndTypedArgs) {
+ // Create a new trace session.
+ auto* tracing_session = NewTraceWithCategories({"foo"});
+ tracing_session->get()->StartBlocking();
+
+ TRACE_EVENT_BEGIN("foo", "E", "arg", "value",
+ perfetto::protos::pbzero::TrackEvent::kLogMessage,
+ LogMessage());
+ TRACE_EVENT_END("foo");
+
+ tracing_session->get()->StopBlocking();
+
+ std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+ std::string trace(raw_trace.data(), raw_trace.size());
+
+ perfetto::protos::gen::Trace parsed_trace;
+ ASSERT_TRUE(parsed_trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+ // Find typed argument.
+ bool found_args = false;
+ for (const auto& packet : parsed_trace.packet()) {
+ if (!packet.has_track_event())
+ continue;
+ const auto& track_event = packet.track_event();
+ if (track_event.type() !=
+ perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN) {
+ continue;
+ }
+
+ EXPECT_TRUE(track_event.has_log_message());
+ const auto& log = track_event.log_message();
+ EXPECT_EQ(1u, log.source_location_iid());
+ EXPECT_EQ(2u, log.body_iid());
+ found_args = true;
+ }
+ EXPECT_TRUE(found_args);
+
+ // Find untyped argument.
+ EXPECT_THAT(ReadSlicesFromTrace(raw_trace),
+ ElementsAre("B:foo.E(arg=(string)value)"));
+}
+
struct InternedLogMessageBody
: public perfetto::TrackEventInternedDataIndex<
InternedLogMessageBody,