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 <functional> 21 22 #include "perfetto/base/status.h" 23 #include "perfetto/protozero/field.h" 24 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h" 25 #include "src/trace_processor/util/descriptors.h" 26 27 namespace perfetto { 28 namespace trace_processor { 29 30 // TODO(altimin): Move InternedMessageView into trace_processor/util. 31 class InternedMessageView; 32 class PacketSequenceStateGeneration; 33 34 namespace util { 35 36 // ProtoToArgsParser encapsulates the process of taking an arbitrary proto and 37 // parsing it into key-value arg pairs. This is done by traversing 38 // the proto using reflection (with descriptors from |descriptor_pool|) 39 // and passing the parsed data to |Delegate| callbacks. 40 // 41 // E.g. given a proto like 42 // 43 // package perfetto.protos; 44 // message SubMessage { 45 // optional int32 field = 1; 46 // } 47 // message MainMessage { 48 // optional int32 field1 = 1; 49 // optional string field2 = 2; 50 // optional SubMessage field3 = 3; 51 // } 52 // 53 // We will get the args set columns "field1", "field2", "field3.field" and will 54 // store the values found inside as the result. 55 // 56 // Usage of this is as follows: 57 // 58 // DescriptorPool pool; 59 // ProtoToArgsParser parser(pool); 60 // pool.AddProtoFileDescriptor( 61 // /* provide descriptor generated by tools/gen_binary_descriptors */); 62 // parser.ParseMessage(const_bytes, ".perfetto.protos.MainMessage", 63 // /* fields */, /* delegate */); 64 class ProtoToArgsParser { 65 public: 66 explicit ProtoToArgsParser(const DescriptorPool& descriptor_pool); 67 68 struct Key { 69 Key(const std::string& flat_key, const std::string& key); 70 Key(const std::string& key); 71 Key(); 72 ~Key(); 73 74 std::string flat_key; 75 std::string key; 76 }; 77 78 class Delegate { 79 public: 80 virtual ~Delegate(); 81 82 virtual void AddInteger(const Key& key, int64_t value) = 0; 83 virtual void AddUnsignedInteger(const Key& key, uint64_t value) = 0; 84 virtual void AddString(const Key& key, 85 const protozero::ConstChars& value) = 0; 86 virtual void AddString(const Key& key, const std::string& value) = 0; 87 virtual void AddDouble(const Key& key, double value) = 0; 88 virtual void AddPointer(const Key& key, const void* value) = 0; 89 virtual void AddBoolean(const Key& key, bool value) = 0; 90 // Returns whether an entry was added or not. 91 virtual bool AddJson(const Key& key, 92 const protozero::ConstChars& value) = 0; 93 virtual void AddNull(const Key& key) = 0; 94 95 virtual size_t GetArrayEntryIndex(const std::string& array_key) = 0; 96 virtual size_t IncrementArrayEntryIndex(const std::string& array_key) = 0; 97 98 virtual PacketSequenceStateGeneration* seq_state() = 0; 99 packet_timestamp()100 virtual int64_t packet_timestamp() { return 0; } 101 102 template <typename FieldMetadata> GetInternedMessage(FieldMetadata,uint64_t iid)103 typename FieldMetadata::cpp_field_type::Decoder* GetInternedMessage( 104 FieldMetadata, 105 uint64_t iid) { 106 static_assert(std::is_base_of<protozero::proto_utils::FieldMetadataBase, 107 FieldMetadata>::value, 108 "Field metadata should be a subclass of FieldMetadataBase"); 109 static_assert(std::is_same<typename FieldMetadata::message_type, 110 protos::pbzero::InternedData>::value, 111 "Field should belong to InternedData proto"); 112 auto* interned_message_view = 113 GetInternedMessageView(FieldMetadata::kFieldId, iid); 114 if (!interned_message_view) 115 return nullptr; 116 return interned_message_view->template GetOrCreateDecoder< 117 typename FieldMetadata::cpp_field_type>(); 118 } 119 120 protected: 121 virtual InternedMessageView* GetInternedMessageView(uint32_t field_id, 122 uint64_t iid) = 0; 123 }; 124 125 // Given a view of bytes that represent a serialized protozero message of 126 // |type| we will parse each field. 127 // 128 // Returns on any error with a status describing the problem. However any 129 // added values before encountering the error will be parsed and forwarded to 130 // the delegate. 131 // 132 // Fields with ids given in |fields| are parsed using reflection, as well 133 // as known (previously registered) extension fields. If |allowed_fields| is a 134 // nullptr, all fields are going to be parsed. 135 // 136 // Note: 137 // |type| must be the fully qualified name, but with a '.' added to the 138 // beginning. I.E. ".perfetto.protos.TrackEvent". And must match one of the 139 // descriptors already added through |AddProtoFileDescriptor|. 140 // 141 // IMPORTANT: currently bytes fields are not supported. 142 // 143 // TODO(b/145578432): Add support for byte fields. 144 base::Status ParseMessage(const protozero::ConstBytes& cb, 145 const std::string& type, 146 const std::vector<uint16_t>* allowed_fields, 147 Delegate& delegate, 148 int* unknown_extensions = nullptr); 149 150 // This class is responsible for resetting the current key prefix to the old 151 // value when deleted or reset. 152 struct ScopedNestedKeyContext { 153 public: 154 ~ScopedNestedKeyContext(); 155 ScopedNestedKeyContext(ScopedNestedKeyContext&&); 156 ScopedNestedKeyContext(const ScopedNestedKeyContext&) = delete; 157 ScopedNestedKeyContext& operator=(const ScopedNestedKeyContext&) = delete; 158 keyScopedNestedKeyContext159 const Key& key() const { return key_; } 160 161 // Clear this context, which strips the latest suffix from |key_| and sets 162 // it to the state before the nested context was created. 163 void RemoveFieldSuffix(); 164 165 private: 166 friend class ProtoToArgsParser; 167 168 ScopedNestedKeyContext(Key& old_value); 169 170 struct ScopedStringAppender; 171 172 Key& key_; 173 std::optional<size_t> old_flat_key_length_ = std::nullopt; 174 std::optional<size_t> old_key_length_ = std::nullopt; 175 }; 176 177 // These methods can be called from parsing overrides to enter nested 178 // contexts. The contexts are left when the returned scope is destroyed or 179 // RemoveFieldSuffix() is called. 180 ScopedNestedKeyContext EnterDictionary(const std::string& key); 181 ScopedNestedKeyContext EnterArray(size_t index); 182 183 using ParsingOverrideForField = 184 std::function<std::optional<base::Status>(const protozero::Field&, 185 Delegate& delegate)>; 186 187 // Installs an override for the field at the specified path. We will invoke 188 // |parsing_override| when the field is encountered. 189 // 190 // The return value of |parsing_override| indicates whether the override 191 // parsed the sub-message and ProtoToArgsParser should skip it (std::nullopt) 192 // or the sub-message should continue to be parsed by ProtoToArgsParser using 193 // the descriptor (base::Status). 194 // 195 // Note |field_path| must be the full path separated by periods. I.E. in the 196 // proto 197 // 198 // message SubMessage { 199 // optional int32 field = 1; 200 // } 201 // message MainMessage { 202 // optional SubMessage field1 = 1; 203 // optional SubMessage field2 = 2; 204 // } 205 // 206 // To override the handling of both SubMessage fields you must add two parsing 207 // overrides. One with a |field_path| == "field1.field" and another with 208 // "field2.field". 209 void AddParsingOverrideForField(const std::string& field_path, 210 ParsingOverrideForField parsing_override); 211 212 using ParsingOverrideForType = std::function<std::optional<base::Status>( 213 ScopedNestedKeyContext& key, 214 const protozero::ConstBytes& data, 215 Delegate& delegate)>; 216 217 // Installs an override for all fields with the given type. We will invoke 218 // |parsing_override| when a field with the given message type is encountered. 219 // Note that the path-based overrides take precedence over type overrides. 220 // 221 // The return value of |parsing_override| indicates whether the override 222 // parsed the sub-message and ProtoToArgsParser should skip it (std::nullopt) 223 // or the sub-message should continue to be parsed by ProtoToArgsParser using 224 // the descriptor (base::Status). 225 // 226 // 227 // For example, given the following protos and a type override for SubMessage, 228 // all three fields will be parsed using this override. 229 // 230 // message SubMessage { 231 // optional int32 value = 1; 232 // } 233 // 234 // message MainMessage1 { 235 // optional SubMessage field1 = 1; 236 // optional SubMessage field2 = 2; 237 // } 238 // 239 // message MainMessage2 { 240 // optional SubMessage field3 = 1; 241 // } 242 void AddParsingOverrideForType(const std::string& message_type, 243 ParsingOverrideForType parsing_override); 244 245 private: 246 base::Status ParseField(const FieldDescriptor& field_descriptor, 247 int repeated_field_number, 248 protozero::Field field, 249 Delegate& delegate, 250 int* unknown_extensions); 251 252 base::Status ParsePackedField( 253 const FieldDescriptor& field_descriptor, 254 std::unordered_map<size_t, int>& repeated_field_index, 255 protozero::Field field, 256 Delegate& delegate, 257 int* unknown_extensions); 258 259 std::optional<base::Status> MaybeApplyOverrideForField( 260 const protozero::Field&, 261 Delegate& delegate); 262 263 std::optional<base::Status> MaybeApplyOverrideForType( 264 const std::string& message_type, 265 ScopedNestedKeyContext& key, 266 const protozero::ConstBytes& data, 267 Delegate& delegate); 268 269 // A type override can call |key.RemoveFieldSuffix()| if it wants to exclude 270 // the overriden field's name from the parsed args' keys. 271 base::Status ParseMessageInternal(ScopedNestedKeyContext& key, 272 const protozero::ConstBytes& cb, 273 const std::string& type, 274 const std::vector<uint16_t>* fields, 275 Delegate& delegate, 276 int* unknown_extensions); 277 278 base::Status ParseSimpleField(const FieldDescriptor& desciptor, 279 const protozero::Field& field, 280 Delegate& delegate); 281 282 std::unordered_map<std::string, ParsingOverrideForField> field_overrides_; 283 std::unordered_map<std::string, ParsingOverrideForType> type_overrides_; 284 const DescriptorPool& pool_; 285 Key key_prefix_; 286 }; 287 288 } // namespace util 289 } // namespace trace_processor 290 } // namespace perfetto 291 292 #endif // SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_ 293