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