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 bool add_defaults = false); 152 153 // This class is responsible for resetting the current key prefix to the old 154 // value when deleted or reset. 155 struct ScopedNestedKeyContext { 156 public: 157 ~ScopedNestedKeyContext(); 158 ScopedNestedKeyContext(ScopedNestedKeyContext&&) noexcept; 159 ScopedNestedKeyContext(const ScopedNestedKeyContext&) = delete; 160 ScopedNestedKeyContext& operator=(const ScopedNestedKeyContext&) = delete; 161 keyScopedNestedKeyContext162 const Key& key() const { return key_; } 163 164 // Clear this context, which strips the latest suffix from |key_| and sets 165 // it to the state before the nested context was created. 166 void RemoveFieldSuffix(); 167 168 private: 169 friend class ProtoToArgsParser; 170 171 explicit ScopedNestedKeyContext(Key& old_value); 172 173 struct ScopedStringAppender; 174 175 Key& key_; 176 std::optional<size_t> old_flat_key_length_ = std::nullopt; 177 std::optional<size_t> old_key_length_ = std::nullopt; 178 }; 179 180 // These methods can be called from parsing overrides to enter nested 181 // contexts. The contexts are left when the returned scope is destroyed or 182 // RemoveFieldSuffix() is called. 183 ScopedNestedKeyContext EnterDictionary(const std::string& key); 184 ScopedNestedKeyContext EnterArray(size_t index); 185 186 using ParsingOverrideForField = 187 std::function<std::optional<base::Status>(const protozero::Field&, 188 Delegate& delegate)>; 189 190 // Installs an override for the field at the specified path. We will invoke 191 // |parsing_override| when the field is encountered. 192 // 193 // The return value of |parsing_override| indicates whether the override 194 // parsed the sub-message and ProtoToArgsParser should skip it (std::nullopt) 195 // or the sub-message should continue to be parsed by ProtoToArgsParser using 196 // the descriptor (base::Status). 197 // 198 // Note |field_path| must be the full path separated by periods. I.E. in the 199 // proto 200 // 201 // message SubMessage { 202 // optional int32 field = 1; 203 // } 204 // message MainMessage { 205 // optional SubMessage field1 = 1; 206 // optional SubMessage field2 = 2; 207 // } 208 // 209 // To override the handling of both SubMessage fields you must add two parsing 210 // overrides. One with a |field_path| == "field1.field" and another with 211 // "field2.field". 212 void AddParsingOverrideForField(const std::string& field_path, 213 ParsingOverrideForField parsing_override); 214 215 using ParsingOverrideForType = std::function<std::optional<base::Status>( 216 ScopedNestedKeyContext& key, 217 const protozero::ConstBytes& data, 218 Delegate& delegate)>; 219 220 // Installs an override for all fields with the given type. We will invoke 221 // |parsing_override| when a field with the given message type is encountered. 222 // Note that the path-based overrides take precedence over type overrides. 223 // 224 // The return value of |parsing_override| indicates whether the override 225 // parsed the sub-message and ProtoToArgsParser should skip it (std::nullopt) 226 // or the sub-message should continue to be parsed by ProtoToArgsParser using 227 // the descriptor (base::Status). 228 // 229 // 230 // For example, given the following protos and a type override for SubMessage, 231 // all three fields will be parsed using this override. 232 // 233 // message SubMessage { 234 // optional int32 value = 1; 235 // } 236 // 237 // message MainMessage1 { 238 // optional SubMessage field1 = 1; 239 // optional SubMessage field2 = 2; 240 // } 241 // 242 // message MainMessage2 { 243 // optional SubMessage field3 = 1; 244 // } 245 void AddParsingOverrideForType(const std::string& message_type, 246 ParsingOverrideForType parsing_override); 247 248 private: 249 base::Status ParseField(const FieldDescriptor& field_descriptor, 250 int repeated_field_number, 251 protozero::Field field, 252 Delegate& delegate, 253 int* unknown_extensions, 254 bool add_defaults); 255 256 base::Status ParsePackedField( 257 const FieldDescriptor& field_descriptor, 258 std::unordered_map<size_t, int>& repeated_field_index, 259 protozero::Field field, 260 Delegate& delegate, 261 int* unknown_extensions, 262 bool add_defaults); 263 264 std::optional<base::Status> MaybeApplyOverrideForField( 265 const protozero::Field&, 266 Delegate& delegate); 267 268 std::optional<base::Status> MaybeApplyOverrideForType( 269 const std::string& message_type, 270 ScopedNestedKeyContext& key, 271 const protozero::ConstBytes& data, 272 Delegate& delegate); 273 274 // A type override can call |key.RemoveFieldSuffix()| if it wants to exclude 275 // the overriden field's name from the parsed args' keys. 276 base::Status ParseMessageInternal(ScopedNestedKeyContext& key, 277 const protozero::ConstBytes& cb, 278 const std::string& type, 279 const std::vector<uint32_t>* fields, 280 Delegate& delegate, 281 int* unknown_extensions, 282 bool add_defaults = false); 283 284 base::Status ParseSimpleField(const FieldDescriptor& desciptor, 285 const protozero::Field& field, 286 Delegate& delegate); 287 288 base::Status AddDefault(const FieldDescriptor& desciptor, Delegate& delegate); 289 290 base::Status AddEnum(const FieldDescriptor& descriptor, 291 int32_t value, 292 Delegate& delegate); 293 294 std::unordered_map<std::string, ParsingOverrideForField> field_overrides_; 295 std::unordered_map<std::string, ParsingOverrideForType> type_overrides_; 296 const DescriptorPool& pool_; 297 Key key_prefix_; 298 }; 299 300 } // namespace util 301 } // namespace trace_processor 302 } // namespace perfetto 303 304 #endif // SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_ 305