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 #include "src/trace_processor/util/debug_annotation_parser.h"
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <ios>
22 #include <map>
23 #include <sstream>
24 #include <string>
25 #include <utility>
26 #include <vector>
27
28 #include "perfetto/base/status.h"
29 #include "perfetto/protozero/field.h"
30 #include "perfetto/protozero/scattered_heap_buffer.h"
31 #include "perfetto/trace_processor/ref_counted.h"
32 #include "perfetto/trace_processor/trace_blob.h"
33 #include "perfetto/trace_processor/trace_blob_view.h"
34 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
35 #include "src/trace_processor/importers/proto/packet_sequence_state_builder.h"
36 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
37 #include "src/trace_processor/storage/trace_storage.h"
38 #include "src/trace_processor/test_messages.descriptor.h"
39 #include "src/trace_processor/types/trace_processor_context.h"
40 #include "src/trace_processor/util/interned_message_view.h"
41 #include "src/trace_processor/util/proto_to_args_parser.h"
42 #include "test/gtest_and_gmock.h"
43
44 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
45 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
46 #include "protos/perfetto/trace/test_event.pbzero.h"
47 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
48
49 namespace perfetto::trace_processor::util {
50 namespace {
51
ParseDebugAnnotation(DebugAnnotationParser & parser,protozero::HeapBuffered<protos::pbzero::DebugAnnotation> & msg,ProtoToArgsParser::Delegate & delegate)52 base::Status ParseDebugAnnotation(
53 DebugAnnotationParser& parser,
54 protozero::HeapBuffered<protos::pbzero::DebugAnnotation>& msg,
55 ProtoToArgsParser::Delegate& delegate) {
56 std::vector<uint8_t> data = msg.SerializeAsArray();
57 return parser.Parse(protozero::ConstBytes{data.data(), data.size()},
58 delegate);
59 }
60
61 class DebugAnnotationParserTest : public ::testing::Test,
62 public ProtoToArgsParser::Delegate {
63 protected:
DebugAnnotationParserTest()64 DebugAnnotationParserTest() { context_.storage.reset(new TraceStorage()); }
65
args() const66 const std::vector<std::string>& args() const { return args_; }
67
InternMessage(uint32_t field_id,TraceBlobView message)68 void InternMessage(uint32_t field_id, TraceBlobView message) {
69 state_builder_.InternMessage(field_id, std::move(message));
70 }
71
72 private:
73 using Key = ProtoToArgsParser::Key;
74
AddInteger(const Key & key,int64_t value)75 void AddInteger(const Key& key, int64_t value) override {
76 std::stringstream ss;
77 ss << key.flat_key << " " << key.key << " " << value;
78 args_.push_back(ss.str());
79 }
80
AddUnsignedInteger(const Key & key,uint64_t value)81 void AddUnsignedInteger(const Key& key, uint64_t value) override {
82 std::stringstream ss;
83 ss << key.flat_key << " " << key.key << " " << value;
84 args_.push_back(ss.str());
85 }
86
AddString(const Key & key,const protozero::ConstChars & value)87 void AddString(const Key& key, const protozero::ConstChars& value) override {
88 std::stringstream ss;
89 ss << key.flat_key << " " << key.key << " " << value.ToStdString();
90 args_.push_back(ss.str());
91 }
92
AddString(const Key & key,const std::string & value)93 void AddString(const Key& key, const std::string& value) override {
94 std::stringstream ss;
95 ss << key.flat_key << " " << key.key << " " << value;
96 args_.push_back(ss.str());
97 }
98
AddDouble(const Key & key,double value)99 void AddDouble(const Key& key, double value) override {
100 std::stringstream ss;
101 ss << key.flat_key << " " << key.key << " " << value;
102 args_.push_back(ss.str());
103 }
104
AddPointer(const Key & key,uint64_t value)105 void AddPointer(const Key& key, uint64_t value) override {
106 std::stringstream ss;
107 ss << key.flat_key << " " << key.key << " " << std::hex << value
108 << std::dec;
109 args_.push_back(ss.str());
110 }
111
AddBoolean(const Key & key,bool value)112 void AddBoolean(const Key& key, bool value) override {
113 std::stringstream ss;
114 ss << key.flat_key << " " << key.key << " " << (value ? "true" : "false");
115 args_.push_back(ss.str());
116 }
117
AddJson(const Key & key,const protozero::ConstChars & value)118 bool AddJson(const Key& key, const protozero::ConstChars& value) override {
119 std::stringstream ss;
120 ss << key.flat_key << " " << key.key << " " << std::hex
121 << value.ToStdString() << std::dec;
122 args_.push_back(ss.str());
123 return true;
124 }
125
AddNull(const Key & key)126 void AddNull(const Key& key) override {
127 std::stringstream ss;
128 ss << key.flat_key << " " << key.key << " [NULL]";
129 args_.push_back(ss.str());
130 }
131
GetArrayEntryIndex(const std::string & array_key)132 size_t GetArrayEntryIndex(const std::string& array_key) final {
133 return array_indices_[array_key];
134 }
135
IncrementArrayEntryIndex(const std::string & array_key)136 size_t IncrementArrayEntryIndex(const std::string& array_key) final {
137 return ++array_indices_[array_key];
138 }
139
GetInternedMessageView(uint32_t field_id,uint64_t iid)140 InternedMessageView* GetInternedMessageView(uint32_t field_id,
141 uint64_t iid) override {
142 return state_builder_.current_generation()->GetInternedMessageView(field_id,
143 iid);
144 }
145
seq_state()146 PacketSequenceStateGeneration* seq_state() final {
147 return state_builder_.current_generation().get();
148 }
149
150 std::vector<std::string> args_;
151 std::map<std::string, size_t> array_indices_;
152
153 TraceProcessorContext context_;
154 PacketSequenceStateBuilder state_builder_{&context_};
155 };
156
157 // This test checks that in when an array is nested inside a dict which is
158 // nested inside an array which is nested inside a dict, flat keys and non-flat
159 // keys are parsed correctly.
TEST_F(DebugAnnotationParserTest,DeeplyNestedDictsAndArrays)160 TEST_F(DebugAnnotationParserTest, DeeplyNestedDictsAndArrays) {
161 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> msg;
162
163 msg->set_name("root");
164 auto* dict1 = msg->add_dict_entries();
165 dict1->set_name("k1");
166 auto* array1 = dict1->add_array_values();
167 auto* dict2 = array1->add_dict_entries();
168 dict2->set_name("k2");
169 auto* array2 = dict2->add_array_values();
170 array2->set_int_value(42);
171
172 DescriptorPool pool;
173 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
174 kTestMessagesDescriptor.size());
175 EXPECT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
176 << status.message();
177
178 ProtoToArgsParser args_parser(pool);
179 DebugAnnotationParser parser(args_parser);
180
181 status = ParseDebugAnnotation(parser, msg, *this);
182 EXPECT_TRUE(status.ok()) << "DebugAnnotationParser::Parse failed with error: "
183 << status.message();
184
185 EXPECT_THAT(args(), testing::ElementsAre("root.k1.k2 root.k1[0].k2[0] 42"));
186 }
187
188 // This test checks that array indexes are correctly merged across messages.
TEST_F(DebugAnnotationParserTest,MergeArrays)189 TEST_F(DebugAnnotationParserTest, MergeArrays) {
190 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> msg1;
191 msg1->set_name("root");
192 auto* item1 = msg1->add_array_values();
193 item1->set_int_value(1);
194
195 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> msg2;
196 msg2->set_name("root");
197 auto* item2 = msg1->add_array_values();
198 item2->set_int_value(2);
199
200 DescriptorPool pool;
201 ProtoToArgsParser args_parser(pool);
202 DebugAnnotationParser parser(args_parser);
203
204 base::Status status = ParseDebugAnnotation(parser, msg1, *this);
205 EXPECT_TRUE(status.ok()) << "DebugAnnotationParser::Parse failed with error: "
206 << status.message();
207
208 status = ParseDebugAnnotation(parser, msg2, *this);
209 EXPECT_TRUE(status.ok()) << "DebugAnnotationParser::Parse failed with error: "
210 << status.message();
211
212 EXPECT_THAT(args(), testing::ElementsAre("root root[0] 1", "root root[1] 2"));
213 }
214
215 // This test checks that nested empty dictionaries / arrays do not cause array
216 // index to be incremented.
TEST_F(DebugAnnotationParserTest,EmptyArrayIndexIsSkipped)217 TEST_F(DebugAnnotationParserTest, EmptyArrayIndexIsSkipped) {
218 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> msg;
219 msg->set_name("root");
220
221 msg->add_array_values()->set_int_value(1);
222
223 // Empty item.
224 msg->add_array_values();
225
226 msg->add_array_values()->set_int_value(3);
227
228 // Empty dict.
229 msg->add_array_values()->add_dict_entries()->set_name("key1");
230
231 auto* nested_dict_entry = msg->add_array_values()->add_dict_entries();
232 nested_dict_entry->set_name("key2");
233 nested_dict_entry->set_string_value("value");
234
235 msg->add_array_values()->set_int_value(5);
236
237 DescriptorPool pool;
238 ProtoToArgsParser args_parser(pool);
239 DebugAnnotationParser parser(args_parser);
240
241 base::Status status = ParseDebugAnnotation(parser, msg, *this);
242 EXPECT_TRUE(status.ok()) << "DebugAnnotationParser::Parse failed with error: "
243 << status.message();
244
245 EXPECT_THAT(args(), testing::ElementsAre("root root[0] 1", "root root[1] 3",
246 "root.key2 root[3].key2 value",
247 "root root[4] 5"));
248 }
249
TEST_F(DebugAnnotationParserTest,NestedArrays)250 TEST_F(DebugAnnotationParserTest, NestedArrays) {
251 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> msg;
252 msg->set_name("root");
253 auto* item1 = msg->add_array_values();
254 item1->add_array_values()->set_int_value(1);
255 item1->add_array_values()->set_int_value(2);
256 auto* item2 = msg->add_array_values();
257 item2->add_array_values()->set_int_value(3);
258 item2->add_array_values()->set_int_value(4);
259
260 DescriptorPool pool;
261 ProtoToArgsParser args_parser(pool);
262 DebugAnnotationParser parser(args_parser);
263
264 base::Status status = ParseDebugAnnotation(parser, msg, *this);
265 EXPECT_TRUE(status.ok()) << "DebugAnnotationParser::Parse failed with error: "
266 << status.message();
267
268 EXPECT_THAT(args(),
269 testing::ElementsAre("root root[0][0] 1", "root root[0][1] 2",
270 "root root[1][0] 3", "root root[1][1] 4"));
271 }
272
TEST_F(DebugAnnotationParserTest,TypedMessageInsideUntyped)273 TEST_F(DebugAnnotationParserTest, TypedMessageInsideUntyped) {
274 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> msg;
275 msg->set_name("root");
276
277 protozero::HeapBuffered<protozero::test::protos::pbzero::EveryField> message;
278 message->set_field_string("value");
279
280 msg->set_proto_type_name(message->GetName());
281 msg->set_proto_value(message.SerializeAsString());
282
283 DescriptorPool pool;
284 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
285 kTestMessagesDescriptor.size());
286 EXPECT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
287 << status.message();
288
289 ProtoToArgsParser args_parser(pool);
290 DebugAnnotationParser parser(args_parser);
291
292 status = ParseDebugAnnotation(parser, msg, *this);
293 EXPECT_TRUE(status.ok()) << "DebugAnnotationParser::Parse failed with error: "
294 << status.message();
295
296 EXPECT_THAT(args(), testing::ElementsAre(
297 "root.field_string root.field_string value"));
298 }
299
TEST_F(DebugAnnotationParserTest,InternedString)300 TEST_F(DebugAnnotationParserTest, InternedString) {
301 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> msg;
302 msg->set_name("root");
303
304 protozero::HeapBuffered<protos::pbzero::InternedString> string;
305 string->set_iid(1);
306 string->set_str("foo");
307 std::vector<uint8_t> data_serialized = string.SerializeAsArray();
308
309 InternMessage(
310 protos::pbzero::InternedData::kDebugAnnotationStringValuesFieldNumber,
311 TraceBlobView(
312 TraceBlob::CopyFrom(data_serialized.data(), data_serialized.size())));
313
314 msg->set_string_value_iid(1);
315
316 DescriptorPool pool;
317 ProtoToArgsParser args_parser(pool);
318 DebugAnnotationParser parser(args_parser);
319
320 auto status = ParseDebugAnnotation(parser, msg, *this);
321 EXPECT_TRUE(status.ok()) << "DebugAnnotationParser::Parse failed with error: "
322 << status.message();
323
324 EXPECT_THAT(args(), testing::ElementsAre("root root foo"));
325 }
326
327 } // namespace
328 } // namespace perfetto::trace_processor::util
329