• 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     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