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     virtual void AddJson(const Key& key,
87                          const protozero::ConstChars& value) = 0;
88 
89     template <typename FieldMetadata>
GetInternedMessage(protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,uint64_t iid)90     typename FieldMetadata::cpp_field_type::Decoder* GetInternedMessage(
91         protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,
92         uint64_t iid) {
93       static_assert(std::is_base_of<protozero::proto_utils::FieldMetadataBase,
94                                     FieldMetadata>::value,
95                     "Field metadata should be a subclass of FieldMetadataBase");
96       static_assert(std::is_same<typename FieldMetadata::message_type,
97                                  protos::pbzero::InternedData>::value,
98                     "Field should belong to InternedData proto");
99       return GetInternedMessageView(FieldMetadata::kFieldId, iid)
100           ->template GetOrCreateDecoder<
101               typename FieldMetadata::cpp_field_type>();
102     }
103 
104    protected:
105     virtual InternedMessageView* GetInternedMessageView(uint32_t field_id,
106                                                         uint64_t iid) = 0;
107   };
108 
109   using ParsingOverride =
110       std::function<base::Optional<base::Status>(const protozero::Field&,
111                                                  Delegate& delegate)>;
112 
113   // Installs an override for the field at the specified path. We will invoke
114   // |parsing_override| when the field is encountered.
115   //
116   // The return value of |parsing_override| indicates whether the override
117   // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt)
118   // or the sub-message should continue to be parsed by ProtoToArgsParser using
119   // the descriptor (base::Status).
120   //
121   // Note |field_path| must be the full path separated by periods. I.E. in the
122   // proto
123   //
124   // message SubMessage {
125   //   optional int32 field = 1;
126   // }
127   // message MainMessage {
128   //   optional SubMessage field1 = 1;
129   //   optional SubMessage field2 = 2;
130   // }
131   //
132   // To override the handling of both SubMessage fields you must add two parsing
133   // overrides. One with a |field_path| == "field1.field" and another with
134   // "field2.field".
135   void AddParsingOverride(std::string field_path,
136                           ParsingOverride parsing_override);
137 
138   // Given a view of bytes that represent a serialized protozero message of
139   // |type| we will parse each field.
140   //
141   // Returns on any error with a status describing the problem. However any
142   // added values before encountering the error will be parsed and forwarded to
143   // the delegate.
144   //
145   // Fields with ids given in |fields| are parsed using reflection, as well
146   // as known (previously registered) extension fields. If |allowed_fields| is a
147   // nullptr, all fields are going to be parsed.
148   //
149   // Note:
150   // |type| must be the fully qualified name, but with a '.' added to the
151   // beginning. I.E. ".perfetto.protos.TrackEvent". And must match one of the
152   // descriptors already added through |AddProtoFileDescriptor|.
153   //
154   // IMPORTANT: currently bytes fields are not supported.
155   //
156   // TODO(b/145578432): Add support for byte fields.
157   base::Status ParseMessage(const protozero::ConstBytes& cb,
158                             const std::string& type,
159                             const std::vector<uint16_t>* allowed_fields,
160                             Delegate& delegate);
161 
162  private:
163   base::Status ParseField(const FieldDescriptor& field_descriptor,
164                           int repeated_field_number,
165                           protozero::Field field,
166                           Delegate& delegate);
167 
168   base::Optional<base::Status> MaybeApplyOverride(const protozero::Field&,
169                                                   Delegate& delegate);
170 
171   base::Status ParseSimpleField(const FieldDescriptor& desciptor,
172                                 const protozero::Field& field,
173                                 Delegate& delegate);
174 
175   std::unordered_map<std::string, ParsingOverride> overrides_;
176   const DescriptorPool& pool_;
177   Key key_prefix_;
178 };
179 
180 }  // namespace util
181 }  // namespace trace_processor
182 }  // namespace perfetto
183 
184 #endif  // SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_
185