xref: /aosp_15_r20/external/perfetto/src/trace_processor/util/proto_to_args_parser.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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