Merge "Support writing untyped annotations into typed contexts."
diff --git a/include/perfetto/tracing/traced_proto.h b/include/perfetto/tracing/traced_proto.h
index 44e4fb2..a52972b 100644
--- a/include/perfetto/tracing/traced_proto.h
+++ b/include/perfetto/tracing/traced_proto.h
@@ -20,6 +20,7 @@
#include "perfetto/base/template_util.h"
#include "perfetto/protozero/field_writer.h"
#include "perfetto/protozero/proto_utils.h"
+#include "perfetto/tracing/traced_value.h"
namespace perfetto {
class EventContext;
@@ -45,7 +46,7 @@
// TracedValue::WriteProto<T>() or implicitly.
// - If a proto message has a repeating DebugAnnotation debug_annotations
// field, it can be filled using the TracedDictionary obtained from
-// TracedProto::WriteDebugAnnotations.
+// TracedProto::AddDebugAnnotations.
template <typename MessageType>
class TracedProto {
public:
@@ -61,6 +62,31 @@
EventContext& context() const { return context_; }
+ // Write additional untyped values into the same context, which is useful
+ // when a given C++ class has a typed representation, but also either has
+ // members which can only be written into an untyped context (e.g. they are
+ // autogenerated) or it's desirable to have a way to quickly extend the
+ // trace representation of this class (e.g. for debugging).
+ //
+ // The usage of the returned TracedDictionary should not be interleaved with
+ // writing into |message| as this results in an inefficient proto layout. To
+ // enforce this, AddDebugAnnotations should be called on TracedProto&&, i.e.
+ // std::move(message).AddDebugAnnotations().
+ //
+ // This requires a 'repeated DebugAnnotations debug_annotations' field in
+ // MessageType.
+ template <typename Check = void>
+ TracedDictionary AddDebugAnnotations() && {
+ static_assert(
+ std::is_base_of<
+ protozero::proto_utils::FieldMetadataBase,
+ typename MessageType::FieldMetadata_DebugAnnotations>::value,
+ "This message does not have a |debug_annotations| field. Please add a"
+ "'repeated perfetto.protos.DebugAnnotation debug_annnotations = N;' "
+ "field to your message.");
+ return TracedDictionary(message_, MessageType::kDebugAnnotations, nullptr);
+ }
+
private:
friend class EventContext;
diff --git a/include/perfetto/tracing/traced_value.h b/include/perfetto/tracing/traced_value.h
index b1a14d3..53f1cb6 100644
--- a/include/perfetto/tracing/traced_value.h
+++ b/include/perfetto/tracing/traced_value.h
@@ -20,6 +20,8 @@
#include "perfetto/base/compiler.h"
#include "perfetto/base/export.h"
#include "perfetto/base/template_util.h"
+#include "perfetto/protozero/message.h"
+#include "perfetto/protozero/proto_utils.h"
#include "perfetto/tracing/internal/checked_scope.h"
#include "perfetto/tracing/string_helpers.h"
#include "perfetto/tracing/traced_value_forward.h"
@@ -246,12 +248,39 @@
private:
friend class TracedValue;
+ template <typename T>
+ friend class TracedProto;
- inline explicit TracedDictionary(protos::pbzero::DebugAnnotation* context,
- internal::CheckedScope* parent_scope)
- : context_(context), checked_scope_(parent_scope) {}
+ // Create a |TracedDictionary| which will populate the given field of the
+ // given |message|.
+ template <typename MessageType, typename FieldMetadata>
+ inline explicit TracedDictionary(
+ MessageType* message,
+ protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,
+ internal::CheckedScope* parent_scope)
+ : message_(message),
+ field_id_(FieldMetadata::kFieldId),
+ checked_scope_(parent_scope) {
+ static_assert(std::is_base_of<protozero::Message, MessageType>::value,
+ "Message should be a subclass of protozero::Message");
+ static_assert(std::is_base_of<protozero::proto_utils::FieldMetadataBase,
+ FieldMetadata>::value,
+ "FieldMetadata should be a subclass of FieldMetadataBase");
+ static_assert(
+ std::is_same<typename FieldMetadata::message_type, MessageType>::value,
+ "Field does not belong to this message");
+ static_assert(
+ std::is_same<typename FieldMetadata::cpp_field_type,
+ ::perfetto::protos::pbzero::DebugAnnotation>::value,
+ "Field should be of DebugAnnotation type");
+ static_assert(
+ FieldMetadata::kRepetitionType ==
+ protozero::proto_utils::RepetitionType::kRepeatedNotPacked,
+ "Field should be non-packed repeated");
+ }
- protos::pbzero::DebugAnnotation* context_;
+ protozero::Message* const message_;
+ const uint32_t field_id_;
internal::CheckedScope checked_scope_;
};
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 7fe24f3..4ee4df6 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -8397,6 +8397,8 @@
// When 0 this is the bottom-most nested message.
optional uint32 remaining_nesting_depth = 3;
+
+ repeated DebugAnnotation debug_annotations = 7;
}
optional TestPayload payload = 5;
}
diff --git a/protos/perfetto/trace/test_event.proto b/protos/perfetto/trace/test_event.proto
index 67a8f60..023343e 100644
--- a/protos/perfetto/trace/test_event.proto
+++ b/protos/perfetto/trace/test_event.proto
@@ -16,6 +16,8 @@
syntax = "proto2";
+import "protos/perfetto/trace/track_event/debug_annotation.proto";
+
package perfetto.protos;
// Event used by testing code.
@@ -43,6 +45,8 @@
// When 0 this is the bottom-most nested message.
optional uint32 remaining_nesting_depth = 3;
+
+ repeated DebugAnnotation debug_annotations = 7;
}
optional TestPayload payload = 5;
}
diff --git a/protos/perfetto/trace/test_extensions.proto b/protos/perfetto/trace/test_extensions.proto
index 94118dc..dca2db6 100644
--- a/protos/perfetto/trace/test_extensions.proto
+++ b/protos/perfetto/trace/test_extensions.proto
@@ -17,6 +17,7 @@
syntax = "proto2";
import public "protos/perfetto/trace/track_event/track_event.proto";
+import "protos/perfetto/trace/track_event/debug_annotation.proto";
package perfetto.protos;
@@ -35,4 +36,6 @@
message TestExtensionChild {
optional string child_field_for_testing = 1;
+
+ repeated DebugAnnotation debug_annotations = 99;
}
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 8b93efd..7926bd3 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -1360,21 +1360,21 @@
context_->storage->InternString("count"),
context_->storage->InternString("bytes")}} {
// Switch |source_location_iid| into its interned data variant.
- args_parser_.AddParsingOverride(
+ args_parser_.AddParsingOverrideForField(
"begin_impl_frame_args.current_args.source_location_iid",
[](const protozero::Field& field,
util::ProtoToArgsParser::Delegate& delegate) {
return MaybeParseSourceLocation("begin_impl_frame_args.current_args",
field, delegate);
});
- args_parser_.AddParsingOverride(
+ args_parser_.AddParsingOverrideForField(
"begin_impl_frame_args.last_args.source_location_iid",
[](const protozero::Field& field,
util::ProtoToArgsParser::Delegate& delegate) {
return MaybeParseSourceLocation("begin_impl_frame_args.last_args",
field, delegate);
});
- args_parser_.AddParsingOverride(
+ args_parser_.AddParsingOverrideForField(
"begin_frame_observer_state.last_begin_frame_args.source_location_iid",
[](const protozero::Field& field,
util::ProtoToArgsParser::Delegate& delegate) {
@@ -1382,7 +1382,7 @@
"begin_frame_observer_state.last_begin_frame_args", field,
delegate);
});
- args_parser_.AddParsingOverride(
+ args_parser_.AddParsingOverrideForField(
"chrome_memory_pressure_notification.creation_location_iid",
[](const protozero::Field& field,
util::ProtoToArgsParser::Delegate& delegate) {
@@ -1390,6 +1390,18 @@
field, delegate);
});
+ // Parse DebugAnnotations.
+ args_parser_.AddParsingOverrideForType(
+ ".perfetto.protos.DebugAnnotation",
+ [&](util::ProtoToArgsParser::ScopedNestedKeyContext& key,
+ const protozero::ConstBytes& data,
+ util::ProtoToArgsParser::Delegate& delegate) {
+ // Do not add "debug_annotations" to the final key.
+ key.RemoveFieldSuffix();
+ util::DebugAnnotationParser annotation_parser(args_parser_);
+ return annotation_parser.Parse(data, delegate);
+ });
+
for (uint16_t index : kReflectFields) {
reflect_fields_.push_back(index);
}
diff --git a/src/trace_processor/util/debug_annotation_parser.cc b/src/trace_processor/util/debug_annotation_parser.cc
index eeaf39f..7fa359e 100644
--- a/src/trace_processor/util/debug_annotation_parser.cc
+++ b/src/trace_processor/util/debug_annotation_parser.cc
@@ -105,18 +105,15 @@
size_t index = delegate.GetArrayEntryIndex(context_name.key);
bool added_entry = false;
for (auto it = annotation.array_values(); it; ++it) {
+ std::string array_key = context_name.key;
protos::pbzero::DebugAnnotation::Decoder value(*it);
auto nested_key = proto_to_args_parser_.EnterArray(index);
ParseResult value_parse_result =
ParseDebugAnnotationValue(value, delegate, nested_key.key());
- // Reset the key here to ensure that we have the correct array key to
- // increment.
- nested_key.Reset();
-
if (value_parse_result.added_entry) {
- index = delegate.IncrementArrayEntryIndex(context_name.key);
+ index = delegate.IncrementArrayEntryIndex(array_key);
added_entry = true;
}
if (!value_parse_result.status.ok())
diff --git a/src/trace_processor/util/proto_to_args_parser.cc b/src/trace_processor/util/proto_to_args_parser.cc
index 8e4eb22..96e066d 100644
--- a/src/trace_processor/util/proto_to_args_parser.cc
+++ b/src/trace_processor/util/proto_to_args_parser.cc
@@ -26,34 +26,11 @@
namespace {
-// ScopedStringAppender will add |append| to |dest| when constructed and
-// erases the appended suffix from |dest| when it goes out of scope. Thus
-// |dest| must be valid for the entire lifetime of ScopedStringAppender.
-//
-// This is useful as we descend into a proto since the column names just
-// appended with ".field_name" as we go lower.
-//
-// I.E. message1.message2.field_name1 is a column, but we'll then need to
-// append message1.message2.field_name2 afterwards so we only need to append
-// "field_name1" within some scope.
-class ScopedStringAppender {
- public:
- ScopedStringAppender(const std::string& append, std::string* dest)
- : old_size_(dest->size()), dest_(dest) {
- if (dest->empty()) {
- dest_->reserve(append.size());
- } else {
- dest_->reserve(old_size_ + 1 + append.size());
- dest_->append(".");
- }
- dest_->append(append);
- }
- ~ScopedStringAppender() { dest_->erase(old_size_); }
-
- private:
- size_t old_size_;
- std::string* dest_;
-};
+void AppendProtoType(std::string& target, const std::string& value) {
+ if (!target.empty())
+ target += '.';
+ target += value;
+}
} // namespace
@@ -78,10 +55,10 @@
}
ProtoToArgsParser::ScopedNestedKeyContext::~ScopedNestedKeyContext() {
- Reset();
+ RemoveFieldSuffix();
}
-void ProtoToArgsParser::ScopedNestedKeyContext::Reset() {
+void ProtoToArgsParser::ScopedNestedKeyContext::RemoveFieldSuffix() {
if (old_flat_key_length_)
key_.flat_key.resize(old_flat_key_length_.value());
if (old_key_length_)
@@ -103,6 +80,21 @@
const std::string& type,
const std::vector<uint16_t>* allowed_fields,
Delegate& delegate) {
+ ScopedNestedKeyContext key_context(key_prefix_);
+ return ParseMessageInternal(key_context, cb, type, allowed_fields, delegate);
+}
+
+base::Status ProtoToArgsParser::ParseMessageInternal(
+ ScopedNestedKeyContext& key_context,
+ const protozero::ConstBytes& cb,
+ const std::string& type,
+ const std::vector<uint16_t>* allowed_fields,
+ Delegate& delegate) {
+ if (auto override_result =
+ MaybeApplyOverrideForType(type, key_context, cb, delegate)) {
+ return override_result.value();
+ }
+
auto idx = pool_.FindDescriptorIdx(type);
if (!idx) {
return base::Status("Failed to find proto descriptor");
@@ -159,14 +151,14 @@
// In the args table we build up message1.message2.field1 as the column
// name. This will append the ".field1" suffix to |key_prefix| and then
// remove it when it goes out of scope.
- ScopedStringAppender scoped_prefix(prefix_part, &key_prefix_.key);
- ScopedStringAppender scoped_flat_key_prefix(field_descriptor.name(),
- &key_prefix_.flat_key);
+ ScopedNestedKeyContext key_context(key_prefix_);
+ AppendProtoType(key_prefix_.flat_key, field_descriptor.name());
+ AppendProtoType(key_prefix_.key, prefix_part);
// If we have an override parser then use that instead and move onto the
// next loop.
if (base::Optional<base::Status> status =
- MaybeApplyOverride(field, delegate)) {
+ MaybeApplyOverrideForField(field, delegate)) {
return *status;
}
@@ -175,27 +167,45 @@
// recurse into it.
if (field_descriptor.type() ==
protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
- return ParseMessage(field.as_bytes(), field_descriptor.resolved_type_name(),
- nullptr, delegate);
+ return ParseMessageInternal(key_context, field.as_bytes(),
+ field_descriptor.resolved_type_name(), nullptr,
+ delegate);
}
return ParseSimpleField(field_descriptor, field, delegate);
}
-void ProtoToArgsParser::AddParsingOverride(std::string field,
- ParsingOverride func) {
- overrides_[std::move(field)] = std::move(func);
+void ProtoToArgsParser::AddParsingOverrideForField(
+ const std::string& field,
+ ParsingOverrideForField func) {
+ field_overrides_[field] = std::move(func);
}
-base::Optional<base::Status> ProtoToArgsParser::MaybeApplyOverride(
+void ProtoToArgsParser::AddParsingOverrideForType(const std::string& type,
+ ParsingOverrideForType func) {
+ type_overrides_[type] = std::move(func);
+}
+
+base::Optional<base::Status> ProtoToArgsParser::MaybeApplyOverrideForField(
const protozero::Field& field,
Delegate& delegate) {
- auto it = overrides_.find(key_prefix_.flat_key);
- if (it == overrides_.end())
+ auto it = field_overrides_.find(key_prefix_.flat_key);
+ if (it == field_overrides_.end())
return base::nullopt;
return it->second(field, delegate);
}
+base::Optional<base::Status> ProtoToArgsParser::MaybeApplyOverrideForType(
+ const std::string& message_type,
+ ScopedNestedKeyContext& key,
+ const protozero::ConstBytes& data,
+ Delegate& delegate) {
+ auto it = type_overrides_.find(message_type);
+ if (it == type_overrides_.end())
+ return base::nullopt;
+ return it->second(key, data, delegate);
+}
+
base::Status ProtoToArgsParser::ParseSimpleField(
const FieldDescriptor& descriptor,
const protozero::Field& field,
@@ -275,12 +285,8 @@
ProtoToArgsParser::ScopedNestedKeyContext ProtoToArgsParser::EnterDictionary(
const std::string& name) {
auto context = ScopedNestedKeyContext(key_prefix_);
- if (!key_prefix_.key.empty())
- key_prefix_.key += '.';
- key_prefix_.key += name;
- if (!key_prefix_.flat_key.empty())
- key_prefix_.flat_key += '.';
- key_prefix_.flat_key += name;
+ AppendProtoType(key_prefix_.key, name);
+ AppendProtoType(key_prefix_.flat_key, name);
return context;
}
diff --git a/src/trace_processor/util/proto_to_args_parser.h b/src/trace_processor/util/proto_to_args_parser.h
index 9110e91..e6934a6 100644
--- a/src/trace_processor/util/proto_to_args_parser.h
+++ b/src/trace_processor/util/proto_to_args_parser.h
@@ -110,7 +110,64 @@
uint64_t iid) = 0;
};
- using ParsingOverride =
+ // Given a view of bytes that represent a serialized protozero message of
+ // |type| we will parse each field.
+ //
+ // Returns on any error with a status describing the problem. However any
+ // added values before encountering the error will be parsed and forwarded to
+ // the delegate.
+ //
+ // Fields with ids given in |fields| are parsed using reflection, as well
+ // as known (previously registered) extension fields. If |allowed_fields| is a
+ // nullptr, all fields are going to be parsed.
+ //
+ // Note:
+ // |type| must be the fully qualified name, but with a '.' added to the
+ // beginning. I.E. ".perfetto.protos.TrackEvent". And must match one of the
+ // descriptors already added through |AddProtoFileDescriptor|.
+ //
+ // IMPORTANT: currently bytes fields are not supported.
+ //
+ // TODO(b/145578432): Add support for byte fields.
+ base::Status ParseMessage(const protozero::ConstBytes& cb,
+ const std::string& type,
+ const std::vector<uint16_t>* allowed_fields,
+ Delegate& delegate);
+
+ // This class is responsible for resetting the current key prefix to the old
+ // value when deleted or reset.
+ struct ScopedNestedKeyContext {
+ public:
+ ~ScopedNestedKeyContext();
+ ScopedNestedKeyContext(ScopedNestedKeyContext&&);
+ ScopedNestedKeyContext(const ScopedNestedKeyContext&) = delete;
+ ScopedNestedKeyContext& operator=(const ScopedNestedKeyContext&) = delete;
+
+ const Key& key() const { return key_; }
+
+ // Clear this context, which strips the latest suffix from |key_| and sets
+ // it to the state before the nested context was created.
+ void RemoveFieldSuffix();
+
+ private:
+ friend class ProtoToArgsParser;
+
+ ScopedNestedKeyContext(Key& old_value);
+
+ struct ScopedStringAppender;
+
+ Key& key_;
+ base::Optional<size_t> old_flat_key_length_ = base::nullopt;
+ base::Optional<size_t> old_key_length_ = base::nullopt;
+ };
+
+ // These methods can be called from parsing overrides to enter nested
+ // contexts. The contexts are left when the returned scope is destroyed or
+ // RemoveFieldSuffix() is called.
+ ScopedNestedKeyContext EnterDictionary(const std::string& key);
+ ScopedNestedKeyContext EnterArray(size_t index);
+
+ using ParsingOverrideForField =
std::function<base::Optional<base::Status>(const protozero::Field&,
Delegate& delegate)>;
@@ -136,63 +193,41 @@
// To override the handling of both SubMessage fields you must add two parsing
// overrides. One with a |field_path| == "field1.field" and another with
// "field2.field".
- void AddParsingOverride(std::string field_path,
- ParsingOverride parsing_override);
+ void AddParsingOverrideForField(const std::string& field_path,
+ ParsingOverrideForField parsing_override);
- // Given a view of bytes that represent a serialized protozero message of
- // |type| we will parse each field.
+ using ParsingOverrideForType = std::function<base::Optional<base::Status>(
+ ScopedNestedKeyContext& key,
+ const protozero::ConstBytes& data,
+ Delegate& delegate)>;
+
+ // Installs an override for all fields with the given type. We will invoke
+ // |parsing_override| when a field with the given message type is encountered.
+ // Note that the path-based overrides take precedence over type overrides.
//
- // Returns on any error with a status describing the problem. However any
- // added values before encountering the error will be parsed and forwarded to
- // the delegate.
+ // The return value of |parsing_override| indicates whether the override
+ // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt)
+ // or the sub-message should continue to be parsed by ProtoToArgsParser using
+ // the descriptor (base::Status).
//
- // Fields with ids given in |fields| are parsed using reflection, as well
- // as known (previously registered) extension fields. If |allowed_fields| is a
- // nullptr, all fields are going to be parsed.
//
- // Note:
- // |type| must be the fully qualified name, but with a '.' added to the
- // beginning. I.E. ".perfetto.protos.TrackEvent". And must match one of the
- // descriptors already added through |AddProtoFileDescriptor|.
+ // For example, given the following protos and a type override for SubMessage,
+ // all three fields will be parsed using this override.
//
- // IMPORTANT: currently bytes fields are not supported.
+ // message SubMessage {
+ // optional int32 value = 1;
+ // }
//
- // TODO(b/145578432): Add support for byte fields.
- base::Status ParseMessage(const protozero::ConstBytes& cb,
- const std::string& type,
- const std::vector<uint16_t>* allowed_fields,
- Delegate& delegate);
-
- struct ScopedNestedKeyContext {
- public:
- ~ScopedNestedKeyContext();
- ScopedNestedKeyContext(ScopedNestedKeyContext&&);
- ScopedNestedKeyContext(const ScopedNestedKeyContext&) = delete;
- ScopedNestedKeyContext& operator=(const ScopedNestedKeyContext&) = delete;
-
- const Key& key() const { return key_; }
-
- // Reset this context, which sets |key_| to the state before the nested
- // context was created.
- void Reset();
-
- private:
- friend class ProtoToArgsParser;
-
- ScopedNestedKeyContext(Key& old_value);
-
- struct ScopedStringAppender;
-
- Key& key_;
- base::Optional<size_t> old_flat_key_length_ = base::nullopt;
- base::Optional<size_t> old_key_length_ = base::nullopt;
- };
-
- // These methods can be called from parsing overrides to enter nested
- // contexts. The contexts are left when the returned scope is destroyed or
- // reset.
- ScopedNestedKeyContext EnterDictionary(const std::string& key);
- ScopedNestedKeyContext EnterArray(size_t index);
+ // message MainMessage1 {
+ // optional SubMessage field1 = 1;
+ // optional SubMessage field2 = 2;
+ // }
+ //
+ // message MainMessage2 {
+ // optional SubMessage field3 = 1;
+ // }
+ void AddParsingOverrideForType(const std::string& message_type,
+ ParsingOverrideForType parsing_override);
private:
base::Status ParseField(const FieldDescriptor& field_descriptor,
@@ -200,14 +235,30 @@
protozero::Field field,
Delegate& delegate);
- base::Optional<base::Status> MaybeApplyOverride(const protozero::Field&,
- Delegate& delegate);
+ base::Optional<base::Status> MaybeApplyOverrideForField(
+ const protozero::Field&,
+ Delegate& delegate);
+
+ base::Optional<base::Status> MaybeApplyOverrideForType(
+ const std::string& message_type,
+ ScopedNestedKeyContext& key,
+ const protozero::ConstBytes& data,
+ Delegate& delegate);
+
+ // A type override can call |key.RemoveFieldSuffix()| if it wants to exclude
+ // the overriden field's name from the parsed args' keys.
+ base::Status ParseMessageInternal(ScopedNestedKeyContext& key,
+ const protozero::ConstBytes& cb,
+ const std::string& type,
+ const std::vector<uint16_t>* fields,
+ Delegate& delegate);
base::Status ParseSimpleField(const FieldDescriptor& desciptor,
const protozero::Field& field,
Delegate& delegate);
- std::unordered_map<std::string, ParsingOverride> overrides_;
+ std::unordered_map<std::string, ParsingOverrideForField> field_overrides_;
+ std::unordered_map<std::string, ParsingOverrideForType> type_overrides_;
const DescriptorPool& pool_;
Key key_prefix_;
};
diff --git a/src/trace_processor/util/proto_to_args_parser_unittest.cc b/src/trace_processor/util/proto_to_args_parser_unittest.cc
index a8d17f6..8171742 100644
--- a/src/trace_processor/util/proto_to_args_parser_unittest.cc
+++ b/src/trace_processor/util/proto_to_args_parser_unittest.cc
@@ -35,10 +35,9 @@
constexpr size_t kChunkSize = 42;
-using ::testing::_;
-using ::testing::Eq;
-using ::testing::Invoke;
-using ::testing::NiceMock;
+protozero::ConstChars ToChars(const char* str) {
+ return protozero::ConstChars{str, strlen(str)};
+}
class ProtoToArgsParserTest : public ::testing::Test,
public ProtoToArgsParser::Delegate {
@@ -254,7 +253,7 @@
ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
- parser.AddParsingOverride(
+ parser.AddParsingOverrideForField(
"super_nested.value_c",
[](const protozero::Field& field, ProtoToArgsParser::Delegate& writer) {
EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
@@ -290,7 +289,7 @@
ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
- parser.AddParsingOverride(
+ parser.AddParsingOverrideForField(
"super_nested.value_c",
[](const protozero::Field& field, ProtoToArgsParser::Delegate&) {
static int val = 0;
@@ -341,7 +340,7 @@
ProtoToArgsParser parser(pool);
// Now we override the behaviour of |value_c| so we can expand the iid into
// multiple args rows.
- parser.AddParsingOverride(
+ parser.AddParsingOverrideForField(
"super_nested.value_c",
[](const protozero::Field& field, ProtoToArgsParser::Delegate& delegate)
-> base::Optional<base::Status> {
@@ -367,6 +366,79 @@
"line_number line_number 2"));
}
+TEST_F(ProtoToArgsParserTest, OverrideForType) {
+ using namespace protozero::test::protos::pbzero;
+ protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
+ msg->set_super_nested()->set_value_c(3);
+
+ auto binary_proto = msg.SerializeAsArray();
+
+ DescriptorPool pool;
+ auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
+ kTestMessagesDescriptor.size());
+ ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
+ << status.message();
+
+ ProtoToArgsParser parser(pool);
+
+ parser.AddParsingOverrideForType(
+ ".protozero.test.protos.NestedA.NestedB.NestedC",
+ [](ProtoToArgsParser::ScopedNestedKeyContext&,
+ const protozero::ConstBytes&, Delegate& delegate) {
+ delegate.AddInteger(ProtoToArgsParser::Key("arg"), 42);
+ return base::OkStatus();
+ });
+
+ status = parser.ParseMessage(
+ protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
+ ".protozero.test.protos.NestedA", nullptr, *this);
+ EXPECT_TRUE(status.ok())
+ << "InternProtoFieldsIntoArgsTable failed with error: "
+ << status.message();
+ EXPECT_THAT(args(), testing::ElementsAre("arg arg 42"));
+}
+
+TEST_F(ProtoToArgsParserTest, FieldOverrideTakesPrecedence) {
+ using namespace protozero::test::protos::pbzero;
+ protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
+ msg->set_super_nested()->set_value_c(3);
+
+ auto binary_proto = msg.SerializeAsArray();
+
+ DescriptorPool pool;
+ auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
+ kTestMessagesDescriptor.size());
+ ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
+ << status.message();
+
+ ProtoToArgsParser parser(pool);
+
+ parser.AddParsingOverrideForField(
+ "super_nested",
+ [](const protozero::Field&, ProtoToArgsParser::Delegate& writer) {
+ writer.AddString(ProtoToArgsParser::Key("arg"),
+ ToChars("override-for-field"));
+ return base::OkStatus();
+ });
+
+ parser.AddParsingOverrideForType(
+ ".protozero.test.protos.NestedA.NestedB.NestedC",
+ [](ProtoToArgsParser::ScopedNestedKeyContext&,
+ const protozero::ConstBytes&, Delegate& delegate) {
+ delegate.AddString(ProtoToArgsParser::Key("arg"),
+ ToChars("override-for-type"));
+ return base::OkStatus();
+ });
+
+ status = parser.ParseMessage(
+ protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
+ ".protozero.test.protos.NestedA", nullptr, *this);
+ EXPECT_TRUE(status.ok())
+ << "InternProtoFieldsIntoArgsTable failed with error: "
+ << status.message();
+ EXPECT_THAT(args(), testing::ElementsAre("arg arg override-for-field"));
+}
+
} // namespace
} // namespace util
} // namespace trace_processor
diff --git a/src/tracing/traced_proto_unittest.cc b/src/tracing/traced_proto_unittest.cc
index e5c84f1..ac55400 100644
--- a/src/tracing/traced_proto_unittest.cc
+++ b/src/tracing/traced_proto_unittest.cc
@@ -16,6 +16,7 @@
#include "perfetto/tracing/traced_proto.h"
+#include "perfetto/test/traced_value_test_support.h"
#include "perfetto/tracing/track_event.h"
#include "protos/perfetto/trace/test_event.gen.h"
#include "protos/perfetto/trace/test_event.pb.h"
@@ -87,6 +88,9 @@
struct Foo {
void WriteIntoTrace(TracedProto<TestPayload> message) const {
message->set_single_int(42);
+
+ auto dict = std::move(message).AddDebugAnnotations();
+ dict.Add("arg", "value");
}
};
@@ -114,4 +118,21 @@
EXPECT_EQ(result.nested(1).single_int(), 42);
}
+TEST_F(TracedProtoTest, WriteDebugAnnotations) {
+ protozero::HeapBuffered<protos::pbzero::TestEvent> event;
+ WriteIntoTracedProto(context().Wrap(event.get()),
+ protos::pbzero::TestEvent::kPayload, Foo());
+
+ protos::TestEvent result;
+ result.ParseFromString(event.SerializeAsString());
+
+ protos::DebugAnnotation dict;
+ for (const auto& annotation : result.payload().debug_annotations()) {
+ *dict.add_dict_entries() = annotation;
+ }
+
+ EXPECT_EQ(internal::DebugAnnotationToString(dict.SerializeAsString()),
+ "{arg:value}");
+}
+
} // namespace perfetto
diff --git a/src/tracing/traced_value.cc b/src/tracing/traced_value.cc
index 9ac3fd2..bd8b1ad 100644
--- a/src/tracing/traced_value.cc
+++ b/src/tracing/traced_value.cc
@@ -84,7 +84,9 @@
checked_scope_.Reset();
PERFETTO_DCHECK(!context_->is_finalized());
- return TracedDictionary(context_, checked_scope_.parent_scope());
+ return TracedDictionary(context_,
+ protos::pbzero::DebugAnnotation::kDictEntries,
+ checked_scope_.parent_scope());
}
TracedArray TracedValue::WriteArray() && {
@@ -114,14 +116,16 @@
TracedValue TracedDictionary::AddItem(StaticString key) {
PERFETTO_DCHECK(checked_scope_.is_active());
- protos::pbzero::DebugAnnotation* item = context_->add_dict_entries();
+ protos::pbzero::DebugAnnotation* item =
+ message_->BeginNestedMessage<protos::pbzero::DebugAnnotation>(field_id_);
item->set_name(key.value);
return TracedValue(item, &checked_scope_);
}
TracedValue TracedDictionary::AddItem(DynamicString key) {
PERFETTO_DCHECK(checked_scope_.is_active());
- protos::pbzero::DebugAnnotation* item = context_->add_dict_entries();
+ protos::pbzero::DebugAnnotation* item =
+ message_->BeginNestedMessage<protos::pbzero::DebugAnnotation>(field_id_);
item->set_name(key.value);
return TracedValue(item, &checked_scope_);
}
diff --git a/test/trace_processor/track_event/track_event_typed_args.textproto b/test/trace_processor/track_event/track_event_typed_args.textproto
index 6ff29b1..b2d9208 100644
--- a/test/trace_processor/track_event/track_event_typed_args.textproto
+++ b/test/trace_processor/track_event/track_event_typed_args.textproto
@@ -78,6 +78,17 @@
"should be absent from result"
[perfetto.protos.TestExtension.nested_message_extension_for_testing] {
child_field_for_testing: "nesting test"
+ debug_annotations {
+ name: "arg1"
+ string_value: "value"
+ }
+ debug_annotations {
+ name: "arg2"
+ dict_entries {
+ name: "key"
+ string_value: "value"
+ }
+ }
}
}
}
@@ -120,6 +131,13 @@
type: TYPE_STRING
label: LABEL_OPTIONAL
}
+ field {
+ name: "debug_annotations"
+ number: 99
+ type: TYPE_MESSAGE
+ label: LABEL_REPEATED
+ type_name: ".perfetto.protos.DebugAnnotation"
+ }
}
}
}
diff --git a/test/trace_processor/track_event/track_event_typed_args_args.out b/test/trace_processor/track_event/track_event_typed_args_args.out
index ac7d608..fae18d7 100644
--- a/test/trace_processor/track_event/track_event_typed_args_args.out
+++ b/test/trace_processor/track_event/track_event_typed_args_args.out
@@ -13,6 +13,8 @@
"chrome_latency_info.trace_id","chrome_latency_info.trace_id",7,"[NULL]"
"int_extension_for_testing","int_extension_for_testing[0]",42,"[NULL]"
"int_extension_for_testing","int_extension_for_testing[1]",1337,"[NULL]"
+"nested_message_extension_for_testing.arg1","nested_message_extension_for_testing.arg1","[NULL]","value"
+"nested_message_extension_for_testing.arg2.key","nested_message_extension_for_testing.arg2.key","[NULL]","value"
"nested_message_extension_for_testing.child_field_for_testing","nested_message_extension_for_testing.child_field_for_testing","[NULL]","nesting test"
"string_extension_for_testing","string_extension_for_testing","[NULL]","an extension string!"
"chrome_app_state","chrome_app_state","[NULL]","APP_STATE_FOREGROUND"