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