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