blob: 9110e916b06c0e43462bf9694e60595f12f810ed [file] [log] [blame]
Alexander Timin5a99b5c2021-05-11 22:48:07 +00001/*
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"
Alexander Timin99653b12021-05-11 22:50:00 +000022#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
Alexander Timin5a99b5c2021-05-11 22:48:07 +000023#include "src/trace_processor/util/descriptors.h"
24
25namespace perfetto {
26namespace trace_processor {
Alexander Timin99653b12021-05-11 22:50:00 +000027
28// TODO(altimin): Move InternedMessageView into trace_processor/util.
29class InternedMessageView;
30
Alexander Timin5a99b5c2021-05-11 22:48:07 +000031namespace 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 */);
61class ProtoToArgsParser {
62 public:
63 explicit ProtoToArgsParser(const DescriptorPool& descriptor_pool);
64
65 struct Key {
Alexander Timin99653b12021-05-11 22:50:00 +000066 Key(const std::string& flat_key, const std::string& key);
67 Key(const std::string& key);
68 Key();
69 ~Key();
70
Alexander Timin5a99b5c2021-05-11 22:48:07 +000071 std::string flat_key;
Alexander Timin99653b12021-05-11 22:50:00 +000072 std::string key;
Alexander Timin5a99b5c2021-05-11 22:48:07 +000073 };
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;
Alexander Timin97d87852021-05-17 18:01:33 +000086 // Returns whether an entry was added or not.
87 virtual bool AddJson(const Key& key,
Alexander Timin5a99b5c2021-05-11 22:48:07 +000088 const protozero::ConstChars& value) = 0;
Alexander Timin99653b12021-05-11 22:50:00 +000089
Alexander Timin97d87852021-05-17 18:01:33 +000090 virtual size_t GetArrayEntryIndex(const std::string& array_key) = 0;
91 virtual size_t IncrementArrayEntryIndex(const std::string& array_key) = 0;
92
Alexander Timin99653b12021-05-11 22:50:00 +000093 template <typename FieldMetadata>
94 typename FieldMetadata::cpp_field_type::Decoder* GetInternedMessage(
95 protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,
96 uint64_t iid) {
97 static_assert(std::is_base_of<protozero::proto_utils::FieldMetadataBase,
98 FieldMetadata>::value,
99 "Field metadata should be a subclass of FieldMetadataBase");
100 static_assert(std::is_same<typename FieldMetadata::message_type,
101 protos::pbzero::InternedData>::value,
102 "Field should belong to InternedData proto");
103 return GetInternedMessageView(FieldMetadata::kFieldId, iid)
104 ->template GetOrCreateDecoder<
105 typename FieldMetadata::cpp_field_type>();
106 }
107
108 protected:
109 virtual InternedMessageView* GetInternedMessageView(uint32_t field_id,
110 uint64_t iid) = 0;
Alexander Timin5a99b5c2021-05-11 22:48:07 +0000111 };
112
113 using ParsingOverride =
114 std::function<base::Optional<base::Status>(const protozero::Field&,
115 Delegate& delegate)>;
116
117 // Installs an override for the field at the specified path. We will invoke
118 // |parsing_override| when the field is encountered.
119 //
120 // The return value of |parsing_override| indicates whether the override
121 // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt)
122 // or the sub-message should continue to be parsed by ProtoToArgsParser using
123 // the descriptor (base::Status).
124 //
125 // Note |field_path| must be the full path separated by periods. I.E. in the
126 // proto
127 //
128 // message SubMessage {
129 // optional int32 field = 1;
130 // }
131 // message MainMessage {
132 // optional SubMessage field1 = 1;
133 // optional SubMessage field2 = 2;
134 // }
135 //
136 // To override the handling of both SubMessage fields you must add two parsing
137 // overrides. One with a |field_path| == "field1.field" and another with
138 // "field2.field".
139 void AddParsingOverride(std::string field_path,
140 ParsingOverride parsing_override);
141
142 // Given a view of bytes that represent a serialized protozero message of
143 // |type| we will parse each field.
144 //
145 // Returns on any error with a status describing the problem. However any
146 // added values before encountering the error will be parsed and forwarded to
147 // the delegate.
148 //
149 // Fields with ids given in |fields| are parsed using reflection, as well
150 // as known (previously registered) extension fields. If |allowed_fields| is a
151 // nullptr, all fields are going to be parsed.
152 //
153 // Note:
154 // |type| must be the fully qualified name, but with a '.' added to the
155 // beginning. I.E. ".perfetto.protos.TrackEvent". And must match one of the
156 // descriptors already added through |AddProtoFileDescriptor|.
157 //
158 // IMPORTANT: currently bytes fields are not supported.
159 //
160 // TODO(b/145578432): Add support for byte fields.
161 base::Status ParseMessage(const protozero::ConstBytes& cb,
162 const std::string& type,
163 const std::vector<uint16_t>* allowed_fields,
164 Delegate& delegate);
165
Alexander Timin97d87852021-05-17 18:01:33 +0000166 struct ScopedNestedKeyContext {
167 public:
168 ~ScopedNestedKeyContext();
169 ScopedNestedKeyContext(ScopedNestedKeyContext&&);
170 ScopedNestedKeyContext(const ScopedNestedKeyContext&) = delete;
171 ScopedNestedKeyContext& operator=(const ScopedNestedKeyContext&) = delete;
172
173 const Key& key() const { return key_; }
174
175 // Reset this context, which sets |key_| to the state before the nested
176 // context was created.
177 void Reset();
178
179 private:
180 friend class ProtoToArgsParser;
181
182 ScopedNestedKeyContext(Key& old_value);
183
184 struct ScopedStringAppender;
185
186 Key& key_;
187 base::Optional<size_t> old_flat_key_length_ = base::nullopt;
188 base::Optional<size_t> old_key_length_ = base::nullopt;
189 };
190
191 // These methods can be called from parsing overrides to enter nested
192 // contexts. The contexts are left when the returned scope is destroyed or
193 // reset.
194 ScopedNestedKeyContext EnterDictionary(const std::string& key);
195 ScopedNestedKeyContext EnterArray(size_t index);
196
Alexander Timin5a99b5c2021-05-11 22:48:07 +0000197 private:
198 base::Status ParseField(const FieldDescriptor& field_descriptor,
199 int repeated_field_number,
200 protozero::Field field,
201 Delegate& delegate);
202
203 base::Optional<base::Status> MaybeApplyOverride(const protozero::Field&,
204 Delegate& delegate);
205
206 base::Status ParseSimpleField(const FieldDescriptor& desciptor,
207 const protozero::Field& field,
208 Delegate& delegate);
209
210 std::unordered_map<std::string, ParsingOverride> overrides_;
211 const DescriptorPool& pool_;
212 Key key_prefix_;
213};
214
215} // namespace util
216} // namespace trace_processor
217} // namespace perfetto
218
219#endif // SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_