• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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