1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_ 18 #define SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_ 19 20 #include "perfetto/base/status.h" 21 #include "perfetto/protozero/field.h" 22 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h" 23 #include "src/trace_processor/util/descriptors.h" 24 25 namespace perfetto { 26 namespace trace_processor { 27 28 // TODO(altimin): Move InternedMessageView into trace_processor/util. 29 class InternedMessageView; 30 31 namespace util { 32 33 // ProtoToArgsParser encapsulates the process of taking an arbitrary proto and 34 // parsing it into key-value arg pairs. This is done by traversing 35 // the proto using reflection (with descriptors from |descriptor_pool|) 36 // and passing the parsed data to |Delegate| callbacks. 37 // 38 // E.g. given a proto like 39 // 40 // package perfetto.protos; 41 // message SubMessage { 42 // optional int32 field = 1; 43 // } 44 // message MainMessage { 45 // optional int32 field1 = 1; 46 // optional string field2 = 2; 47 // optional SubMessage field3 = 3; 48 // } 49 // 50 // We will get the args set columns "field1", "field2", "field3.field" and will 51 // store the values found inside as the result. 52 // 53 // Usage of this is as follows: 54 // 55 // DescriptorPool pool; 56 // ProtoToArgsParser parser(&pool); 57 // pool.AddProtoFileDescriptor( 58 // /* provide descriptor generated by tools/gen_binary_descriptors */); 59 // parser.ParseMessage(const_bytes, ".perfetto.protos.MainMessage", 60 // /* fields */, /* delegate */); 61 class ProtoToArgsParser { 62 public: 63 explicit ProtoToArgsParser(const DescriptorPool& descriptor_pool); 64 65 struct Key { 66 Key(const std::string& flat_key, const std::string& key); 67 Key(const std::string& key); 68 Key(); 69 ~Key(); 70 71 std::string flat_key; 72 std::string key; 73 }; 74 75 class Delegate { 76 public: 77 virtual ~Delegate(); 78 79 virtual void AddInteger(const Key& key, int64_t value) = 0; 80 virtual void AddUnsignedInteger(const Key& key, uint64_t value) = 0; 81 virtual void AddString(const Key& key, 82 const protozero::ConstChars& value) = 0; 83 virtual void AddDouble(const Key& key, double value) = 0; 84 virtual void AddPointer(const Key& key, const void* value) = 0; 85 virtual void AddBoolean(const Key& key, bool value) = 0; 86 virtual void AddJson(const Key& key, 87 const protozero::ConstChars& value) = 0; 88 89 template <typename FieldMetadata> GetInternedMessage(protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,uint64_t iid)90 typename FieldMetadata::cpp_field_type::Decoder* GetInternedMessage( 91 protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>, 92 uint64_t iid) { 93 static_assert(std::is_base_of<protozero::proto_utils::FieldMetadataBase, 94 FieldMetadata>::value, 95 "Field metadata should be a subclass of FieldMetadataBase"); 96 static_assert(std::is_same<typename FieldMetadata::message_type, 97 protos::pbzero::InternedData>::value, 98 "Field should belong to InternedData proto"); 99 return GetInternedMessageView(FieldMetadata::kFieldId, iid) 100 ->template GetOrCreateDecoder< 101 typename FieldMetadata::cpp_field_type>(); 102 } 103 104 protected: 105 virtual InternedMessageView* GetInternedMessageView(uint32_t field_id, 106 uint64_t iid) = 0; 107 }; 108 109 using ParsingOverride = 110 std::function<base::Optional<base::Status>(const protozero::Field&, 111 Delegate& delegate)>; 112 113 // Installs an override for the field at the specified path. We will invoke 114 // |parsing_override| when the field is encountered. 115 // 116 // The return value of |parsing_override| indicates whether the override 117 // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt) 118 // or the sub-message should continue to be parsed by ProtoToArgsParser using 119 // the descriptor (base::Status). 120 // 121 // Note |field_path| must be the full path separated by periods. I.E. in the 122 // proto 123 // 124 // message SubMessage { 125 // optional int32 field = 1; 126 // } 127 // message MainMessage { 128 // optional SubMessage field1 = 1; 129 // optional SubMessage field2 = 2; 130 // } 131 // 132 // To override the handling of both SubMessage fields you must add two parsing 133 // overrides. One with a |field_path| == "field1.field" and another with 134 // "field2.field". 135 void AddParsingOverride(std::string field_path, 136 ParsingOverride parsing_override); 137 138 // Given a view of bytes that represent a serialized protozero message of 139 // |type| we will parse each field. 140 // 141 // Returns on any error with a status describing the problem. However any 142 // added values before encountering the error will be parsed and forwarded to 143 // the delegate. 144 // 145 // Fields with ids given in |fields| are parsed using reflection, as well 146 // as known (previously registered) extension fields. If |allowed_fields| is a 147 // nullptr, all fields are going to be parsed. 148 // 149 // Note: 150 // |type| must be the fully qualified name, but with a '.' added to the 151 // beginning. I.E. ".perfetto.protos.TrackEvent". And must match one of the 152 // descriptors already added through |AddProtoFileDescriptor|. 153 // 154 // IMPORTANT: currently bytes fields are not supported. 155 // 156 // TODO(b/145578432): Add support for byte fields. 157 base::Status ParseMessage(const protozero::ConstBytes& cb, 158 const std::string& type, 159 const std::vector<uint16_t>* allowed_fields, 160 Delegate& delegate); 161 162 private: 163 base::Status ParseField(const FieldDescriptor& field_descriptor, 164 int repeated_field_number, 165 protozero::Field field, 166 Delegate& delegate); 167 168 base::Optional<base::Status> MaybeApplyOverride(const protozero::Field&, 169 Delegate& delegate); 170 171 base::Status ParseSimpleField(const FieldDescriptor& desciptor, 172 const protozero::Field& field, 173 Delegate& delegate); 174 175 std::unordered_map<std::string, ParsingOverride> overrides_; 176 const DescriptorPool& pool_; 177 Key key_prefix_; 178 }; 179 180 } // namespace util 181 } // namespace trace_processor 182 } // namespace perfetto 183 184 #endif // SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_ 185