• 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 "perfetto/base/status.h"
21 #include "perfetto/protozero/field.h"
22 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
23 #include "src/trace_processor/util/descriptors.h"
24 
25 namespace perfetto {
26 namespace trace_processor {
27 
28 // TODO(altimin): Move InternedMessageView into trace_processor/util.
29 class InternedMessageView;
30 
31 namespace util {
32 
33 // ProtoToArgsParser encapsulates the process of taking an arbitrary proto and
34 // parsing it into key-value arg pairs. This is done by traversing
35 // the proto using reflection (with descriptors from |descriptor_pool|)
36 // and passing the parsed data to |Delegate| callbacks.
37 //
38 // E.g. given a proto like
39 //
40 // package perfetto.protos;
41 // message SubMessage {
42 //   optional int32 field = 1;
43 // }
44 // message MainMessage {
45 //   optional int32 field1 = 1;
46 //   optional string field2 = 2;
47 //   optional SubMessage field3 = 3;
48 // }
49 //
50 // We will get the args set columns "field1", "field2", "field3.field" and will
51 // store the values found inside as the result.
52 //
53 // Usage of this is as follows:
54 //
55 // DescriptorPool pool;
56 // ProtoToArgsParser parser(&pool);
57 // pool.AddProtoFileDescriptor(
58 //     /* provide descriptor generated by tools/gen_binary_descriptors */);
59 // parser.ParseMessage(const_bytes, ".perfetto.protos.MainMessage",
60 //     /* fields */, /* delegate */);
61 class ProtoToArgsParser {
62  public:
63   explicit ProtoToArgsParser(const DescriptorPool& descriptor_pool);
64 
65   struct Key {
66     Key(const std::string& flat_key, const std::string& key);
67     Key(const std::string& key);
68     Key();
69     ~Key();
70 
71     std::string flat_key;
72     std::string key;
73   };
74 
75   class Delegate {
76    public:
77     virtual ~Delegate();
78 
79     virtual void AddInteger(const Key& key, int64_t value) = 0;
80     virtual void AddUnsignedInteger(const Key& key, uint64_t value) = 0;
81     virtual void AddString(const Key& key,
82                            const protozero::ConstChars& value) = 0;
83     virtual void AddDouble(const Key& key, double value) = 0;
84     virtual void AddPointer(const Key& key, const void* value) = 0;
85     virtual void AddBoolean(const Key& key, bool value) = 0;
86     // Returns whether an entry was added or not.
87     virtual bool AddJson(const Key& key,
88                          const protozero::ConstChars& value) = 0;
89     virtual void AddNull(const Key& key) = 0;
90 
91     virtual size_t GetArrayEntryIndex(const std::string& array_key) = 0;
92     virtual size_t IncrementArrayEntryIndex(const std::string& array_key) = 0;
93 
94     template <typename FieldMetadata>
GetInternedMessage(protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,uint64_t iid)95     typename FieldMetadata::cpp_field_type::Decoder* GetInternedMessage(
96         protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,
97         uint64_t iid) {
98       static_assert(std::is_base_of<protozero::proto_utils::FieldMetadataBase,
99                                     FieldMetadata>::value,
100                     "Field metadata should be a subclass of FieldMetadataBase");
101       static_assert(std::is_same<typename FieldMetadata::message_type,
102                                  protos::pbzero::InternedData>::value,
103                     "Field should belong to InternedData proto");
104       auto* interned_message_view =
105           GetInternedMessageView(FieldMetadata::kFieldId, iid);
106       if (!interned_message_view)
107         return nullptr;
108       return interned_message_view->template GetOrCreateDecoder<
109           typename FieldMetadata::cpp_field_type>();
110     }
111 
112    protected:
113     virtual InternedMessageView* GetInternedMessageView(uint32_t field_id,
114                                                         uint64_t iid) = 0;
115   };
116 
117   // Given a view of bytes that represent a serialized protozero message of
118   // |type| we will parse each field.
119   //
120   // Returns on any error with a status describing the problem. However any
121   // added values before encountering the error will be parsed and forwarded to
122   // the delegate.
123   //
124   // Fields with ids given in |fields| are parsed using reflection, as well
125   // as known (previously registered) extension fields. If |allowed_fields| is a
126   // nullptr, all fields are going to be parsed.
127   //
128   // Note:
129   // |type| must be the fully qualified name, but with a '.' added to the
130   // beginning. I.E. ".perfetto.protos.TrackEvent". And must match one of the
131   // descriptors already added through |AddProtoFileDescriptor|.
132   //
133   // IMPORTANT: currently bytes fields are not supported.
134   //
135   // TODO(b/145578432): Add support for byte fields.
136   base::Status ParseMessage(const protozero::ConstBytes& cb,
137                             const std::string& type,
138                             const std::vector<uint16_t>* allowed_fields,
139                             Delegate& delegate,
140                             int* unknown_extensions = nullptr);
141 
142   // This class is responsible for resetting the current key prefix to the old
143   // value when deleted or reset.
144   struct ScopedNestedKeyContext {
145    public:
146     ~ScopedNestedKeyContext();
147     ScopedNestedKeyContext(ScopedNestedKeyContext&&);
148     ScopedNestedKeyContext(const ScopedNestedKeyContext&) = delete;
149     ScopedNestedKeyContext& operator=(const ScopedNestedKeyContext&) = delete;
150 
keyScopedNestedKeyContext151     const Key& key() const { return key_; }
152 
153     // Clear this context, which strips the latest suffix from |key_| and sets
154     // it to the state before the nested context was created.
155     void RemoveFieldSuffix();
156 
157    private:
158     friend class ProtoToArgsParser;
159 
160     ScopedNestedKeyContext(Key& old_value);
161 
162     struct ScopedStringAppender;
163 
164     Key& key_;
165     base::Optional<size_t> old_flat_key_length_ = base::nullopt;
166     base::Optional<size_t> old_key_length_ = base::nullopt;
167   };
168 
169   // These methods can be called from parsing overrides to enter nested
170   // contexts. The contexts are left when the returned scope is destroyed or
171   // RemoveFieldSuffix() is called.
172   ScopedNestedKeyContext EnterDictionary(const std::string& key);
173   ScopedNestedKeyContext EnterArray(size_t index);
174 
175   using ParsingOverrideForField =
176       std::function<base::Optional<base::Status>(const protozero::Field&,
177                                                  Delegate& delegate)>;
178 
179   // Installs an override for the field at the specified path. We will invoke
180   // |parsing_override| when the field is encountered.
181   //
182   // The return value of |parsing_override| indicates whether the override
183   // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt)
184   // or the sub-message should continue to be parsed by ProtoToArgsParser using
185   // the descriptor (base::Status).
186   //
187   // Note |field_path| must be the full path separated by periods. I.E. in the
188   // proto
189   //
190   // message SubMessage {
191   //   optional int32 field = 1;
192   // }
193   // message MainMessage {
194   //   optional SubMessage field1 = 1;
195   //   optional SubMessage field2 = 2;
196   // }
197   //
198   // To override the handling of both SubMessage fields you must add two parsing
199   // overrides. One with a |field_path| == "field1.field" and another with
200   // "field2.field".
201   void AddParsingOverrideForField(const std::string& field_path,
202                                   ParsingOverrideForField parsing_override);
203 
204   using ParsingOverrideForType = std::function<base::Optional<base::Status>(
205       ScopedNestedKeyContext& key,
206       const protozero::ConstBytes& data,
207       Delegate& delegate)>;
208 
209   // Installs an override for all fields with the given type. We will invoke
210   // |parsing_override| when a field with the given message type is encountered.
211   // Note that the path-based overrides take precedence over type overrides.
212   //
213   // The return value of |parsing_override| indicates whether the override
214   // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt)
215   // or the sub-message should continue to be parsed by ProtoToArgsParser using
216   // the descriptor (base::Status).
217   //
218   //
219   // For example, given the following protos and a type override for SubMessage,
220   // all three fields will be parsed using this override.
221   //
222   // message SubMessage {
223   //   optional int32 value = 1;
224   // }
225   //
226   // message MainMessage1 {
227   //   optional SubMessage field1 = 1;
228   //   optional SubMessage field2 = 2;
229   // }
230   //
231   // message MainMessage2 {
232   //   optional SubMessage field3 = 1;
233   // }
234   void AddParsingOverrideForType(const std::string& message_type,
235                                  ParsingOverrideForType parsing_override);
236 
237  private:
238   base::Status ParseField(const FieldDescriptor& field_descriptor,
239                           int repeated_field_number,
240                           protozero::Field field,
241                           Delegate& delegate,
242                           int* unknown_extensions);
243 
244   base::Optional<base::Status> MaybeApplyOverrideForField(
245       const protozero::Field&,
246       Delegate& delegate);
247 
248   base::Optional<base::Status> MaybeApplyOverrideForType(
249       const std::string& message_type,
250       ScopedNestedKeyContext& key,
251       const protozero::ConstBytes& data,
252       Delegate& delegate);
253 
254   // A type override can call |key.RemoveFieldSuffix()| if it wants to exclude
255   // the overriden field's name from the parsed args' keys.
256   base::Status ParseMessageInternal(ScopedNestedKeyContext& key,
257                                     const protozero::ConstBytes& cb,
258                                     const std::string& type,
259                                     const std::vector<uint16_t>* fields,
260                                     Delegate& delegate,
261                                     int* unknown_extensions);
262 
263   base::Status ParseSimpleField(const FieldDescriptor& desciptor,
264                                 const protozero::Field& field,
265                                 Delegate& delegate);
266 
267   std::unordered_map<std::string, ParsingOverrideForField> field_overrides_;
268   std::unordered_map<std::string, ParsingOverrideForType> type_overrides_;
269   const DescriptorPool& pool_;
270   Key key_prefix_;
271 };
272 
273 }  // namespace util
274 }  // namespace trace_processor
275 }  // namespace perfetto
276 
277 #endif  // SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_
278