• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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