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 // Returns whether an entry was added or not. 87 virtual bool AddJson(const Key& key, 88 const protozero::ConstChars& value) = 0; 89 virtual void AddNull(const Key& key) = 0; 90 91 virtual size_t GetArrayEntryIndex(const std::string& array_key) = 0; 92 virtual size_t IncrementArrayEntryIndex(const std::string& array_key) = 0; 93 94 template <typename FieldMetadata> GetInternedMessage(protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,uint64_t iid)95 typename FieldMetadata::cpp_field_type::Decoder* GetInternedMessage( 96 protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>, 97 uint64_t iid) { 98 static_assert(std::is_base_of<protozero::proto_utils::FieldMetadataBase, 99 FieldMetadata>::value, 100 "Field metadata should be a subclass of FieldMetadataBase"); 101 static_assert(std::is_same<typename FieldMetadata::message_type, 102 protos::pbzero::InternedData>::value, 103 "Field should belong to InternedData proto"); 104 auto* interned_message_view = 105 GetInternedMessageView(FieldMetadata::kFieldId, iid); 106 if (!interned_message_view) 107 return nullptr; 108 return interned_message_view->template GetOrCreateDecoder< 109 typename FieldMetadata::cpp_field_type>(); 110 } 111 112 protected: 113 virtual InternedMessageView* GetInternedMessageView(uint32_t field_id, 114 uint64_t iid) = 0; 115 }; 116 117 // Given a view of bytes that represent a serialized protozero message of 118 // |type| we will parse each field. 119 // 120 // Returns on any error with a status describing the problem. However any 121 // added values before encountering the error will be parsed and forwarded to 122 // the delegate. 123 // 124 // Fields with ids given in |fields| are parsed using reflection, as well 125 // as known (previously registered) extension fields. If |allowed_fields| is a 126 // nullptr, all fields are going to be parsed. 127 // 128 // Note: 129 // |type| must be the fully qualified name, but with a '.' added to the 130 // beginning. I.E. ".perfetto.protos.TrackEvent". And must match one of the 131 // descriptors already added through |AddProtoFileDescriptor|. 132 // 133 // IMPORTANT: currently bytes fields are not supported. 134 // 135 // TODO(b/145578432): Add support for byte fields. 136 base::Status ParseMessage(const protozero::ConstBytes& cb, 137 const std::string& type, 138 const std::vector<uint16_t>* allowed_fields, 139 Delegate& delegate, 140 int* unknown_extensions = nullptr); 141 142 // This class is responsible for resetting the current key prefix to the old 143 // value when deleted or reset. 144 struct ScopedNestedKeyContext { 145 public: 146 ~ScopedNestedKeyContext(); 147 ScopedNestedKeyContext(ScopedNestedKeyContext&&); 148 ScopedNestedKeyContext(const ScopedNestedKeyContext&) = delete; 149 ScopedNestedKeyContext& operator=(const ScopedNestedKeyContext&) = delete; 150 keyScopedNestedKeyContext151 const Key& key() const { return key_; } 152 153 // Clear this context, which strips the latest suffix from |key_| and sets 154 // it to the state before the nested context was created. 155 void RemoveFieldSuffix(); 156 157 private: 158 friend class ProtoToArgsParser; 159 160 ScopedNestedKeyContext(Key& old_value); 161 162 struct ScopedStringAppender; 163 164 Key& key_; 165 base::Optional<size_t> old_flat_key_length_ = base::nullopt; 166 base::Optional<size_t> old_key_length_ = base::nullopt; 167 }; 168 169 // These methods can be called from parsing overrides to enter nested 170 // contexts. The contexts are left when the returned scope is destroyed or 171 // RemoveFieldSuffix() is called. 172 ScopedNestedKeyContext EnterDictionary(const std::string& key); 173 ScopedNestedKeyContext EnterArray(size_t index); 174 175 using ParsingOverrideForField = 176 std::function<base::Optional<base::Status>(const protozero::Field&, 177 Delegate& delegate)>; 178 179 // Installs an override for the field at the specified path. We will invoke 180 // |parsing_override| when the field is encountered. 181 // 182 // The return value of |parsing_override| indicates whether the override 183 // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt) 184 // or the sub-message should continue to be parsed by ProtoToArgsParser using 185 // the descriptor (base::Status). 186 // 187 // Note |field_path| must be the full path separated by periods. I.E. in the 188 // proto 189 // 190 // message SubMessage { 191 // optional int32 field = 1; 192 // } 193 // message MainMessage { 194 // optional SubMessage field1 = 1; 195 // optional SubMessage field2 = 2; 196 // } 197 // 198 // To override the handling of both SubMessage fields you must add two parsing 199 // overrides. One with a |field_path| == "field1.field" and another with 200 // "field2.field". 201 void AddParsingOverrideForField(const std::string& field_path, 202 ParsingOverrideForField parsing_override); 203 204 using ParsingOverrideForType = std::function<base::Optional<base::Status>( 205 ScopedNestedKeyContext& key, 206 const protozero::ConstBytes& data, 207 Delegate& delegate)>; 208 209 // Installs an override for all fields with the given type. We will invoke 210 // |parsing_override| when a field with the given message type is encountered. 211 // Note that the path-based overrides take precedence over type overrides. 212 // 213 // The return value of |parsing_override| indicates whether the override 214 // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt) 215 // or the sub-message should continue to be parsed by ProtoToArgsParser using 216 // the descriptor (base::Status). 217 // 218 // 219 // For example, given the following protos and a type override for SubMessage, 220 // all three fields will be parsed using this override. 221 // 222 // message SubMessage { 223 // optional int32 value = 1; 224 // } 225 // 226 // message MainMessage1 { 227 // optional SubMessage field1 = 1; 228 // optional SubMessage field2 = 2; 229 // } 230 // 231 // message MainMessage2 { 232 // optional SubMessage field3 = 1; 233 // } 234 void AddParsingOverrideForType(const std::string& message_type, 235 ParsingOverrideForType parsing_override); 236 237 private: 238 base::Status ParseField(const FieldDescriptor& field_descriptor, 239 int repeated_field_number, 240 protozero::Field field, 241 Delegate& delegate, 242 int* unknown_extensions); 243 244 base::Optional<base::Status> MaybeApplyOverrideForField( 245 const protozero::Field&, 246 Delegate& delegate); 247 248 base::Optional<base::Status> MaybeApplyOverrideForType( 249 const std::string& message_type, 250 ScopedNestedKeyContext& key, 251 const protozero::ConstBytes& data, 252 Delegate& delegate); 253 254 // A type override can call |key.RemoveFieldSuffix()| if it wants to exclude 255 // the overriden field's name from the parsed args' keys. 256 base::Status ParseMessageInternal(ScopedNestedKeyContext& key, 257 const protozero::ConstBytes& cb, 258 const std::string& type, 259 const std::vector<uint16_t>* fields, 260 Delegate& delegate, 261 int* unknown_extensions); 262 263 base::Status ParseSimpleField(const FieldDescriptor& desciptor, 264 const protozero::Field& field, 265 Delegate& delegate); 266 267 std::unordered_map<std::string, ParsingOverrideForField> field_overrides_; 268 std::unordered_map<std::string, ParsingOverrideForType> type_overrides_; 269 const DescriptorPool& pool_; 270 Key key_prefix_; 271 }; 272 273 } // namespace util 274 } // namespace trace_processor 275 } // namespace perfetto 276 277 #endif // SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_ 278