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