blob: 467620d522a9eddcdc3264a88063c7a26f8df934 [file] [log] [blame]
Stephen Nuskobc76a6c2019-12-03 11:55:27 +00001/*
2 * Copyright (C) 2019 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/importers/proto/args_table_utils.h"
18
19#include "perfetto/ext/base/string_view.h"
20#include "perfetto/protozero/scattered_heap_buffer.h"
21#include "protos/perfetto/common/descriptor.pbzero.h"
22#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
23#include "protos/perfetto/trace/track_event/source_location.pbzero.h"
24#include "src/protozero/test/example_proto/test_messages.descriptor.h"
25#include "src/protozero/test/example_proto/test_messages.pbzero.h"
26#include "src/trace_processor/args_tracker.h"
27#include "src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h"
28#include "src/trace_processor/trace_storage.h"
29#include "test/gtest_and_gmock.h"
30
31namespace perfetto {
32namespace trace_processor {
33namespace {
34
35constexpr size_t kChunkSize = 42;
36
37using ::testing::_;
38using ::testing::Eq;
39using ::testing::Invoke;
40using ::testing::NiceMock;
41
42class ArgsTableUtilsTest : public ::testing::Test {
43 protected:
44 ArgsTableUtilsTest() {
45 context_.storage.reset(new TraceStorage);
46 storage_ = context_.storage.get();
47 context_.args_tracker.reset(new ArgsTracker(&context_));
48 sequence_state_.reset(new PacketSequenceState(&context_));
49 }
50
51 bool HasArg(ArgSetId set_id, const base::StringView& key, Variadic value) {
52 const auto& args = storage_->args();
53 auto key_id = storage_->string_pool().GetId(key);
54 EXPECT_TRUE(key_id);
55 auto rows =
56 std::equal_range(args.set_ids().begin(), args.set_ids().end(), set_id);
57 bool found = false;
58 for (; rows.first != rows.second; rows.first++) {
59 size_t index = static_cast<size_t>(
60 std::distance(args.set_ids().begin(), rows.first));
61 if (args.keys()[index] == key_id) {
62 EXPECT_EQ(args.flat_keys()[index], key_id);
63 if (args.flat_keys()[index] == key_id &&
64 args.arg_values()[index] == value) {
65 found = true;
66 break;
67 }
68 }
69 }
70 return found;
71 }
72
73 uint32_t arg_set_id_ = 1;
74 std::unique_ptr<PacketSequenceState> sequence_state_;
75 TraceProcessorContext context_;
76 TraceStorage* storage_;
77};
78
79TEST_F(ArgsTableUtilsTest, EnsureChromeCompositorStateDescriptorParses) {
80 ProtoToArgsTable helper(sequence_state_.get(),
Stephen Nuskoe3c39952019-12-03 15:49:55 +000081 sequence_state_->current_generation(), &context_,
82 nullptr, "", 0);
Stephen Nuskobc76a6c2019-12-03 11:55:27 +000083 auto status = helper.AddProtoFileDescriptor(
84 kChromeCompositorSchedulerStateDescriptor.data(),
85 kChromeCompositorSchedulerStateDescriptor.size());
86 EXPECT_TRUE(status.ok())
87 << "Failed to parse kChromeCompositorSchedulerStateDescriptor: "
88 << status.message();
89}
90
91TEST_F(ArgsTableUtilsTest, EnsureTestMessageProtoParses) {
92 ProtoToArgsTable helper(sequence_state_.get(),
Stephen Nuskoe3c39952019-12-03 15:49:55 +000093 sequence_state_->current_generation(), &context_,
94 nullptr, "", 0);
Stephen Nuskobc76a6c2019-12-03 11:55:27 +000095 auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
96 kTestMessagesDescriptor.size());
97 EXPECT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
98 << status.message();
99}
100
101TEST_F(ArgsTableUtilsTest, BasicSingleLayerProto) {
102 using namespace protozero::test::protos::pbzero;
103 protozero::HeapBuffered<EveryField> msg{kChunkSize, kChunkSize};
104 msg->set_field_int32(-1);
105 msg->set_field_int64(-333123456789ll);
106 msg->set_field_uint32(600);
107 msg->set_field_uint64(333123456789ll);
108 msg->set_field_sint32(-5);
109 msg->set_field_sint64(-9000);
110 msg->set_field_fixed32(12345);
111 msg->set_field_fixed64(444123450000ll);
112 msg->set_field_sfixed32(-69999);
113 msg->set_field_sfixed64(-200);
114 msg->set_field_double(0.5555);
115 msg->set_field_bool(true);
116 msg->set_small_enum(SmallEnum::TO_BE);
117 msg->set_signed_enum(SignedEnum::NEGATIVE);
118 msg->set_big_enum(BigEnum::BEGIN);
119 msg->set_nested_enum(EveryField::PONG);
120 msg->set_field_float(3.14f);
121 msg->set_field_string("FizzBuzz");
122 msg->add_repeated_int32(1);
123 msg->add_repeated_int32(-1);
124 msg->add_repeated_int32(100);
125 msg->add_repeated_int32(2000000);
126
127 auto binary_proto = msg.SerializeAsArray();
128
129 storage_->mutable_track_table()->Insert({});
130 ProtoToArgsTable helper(sequence_state_.get(),
Stephen Nuskoe3c39952019-12-03 15:49:55 +0000131 sequence_state_->current_generation(), &context_,
132 nullptr, "", 0);
Stephen Nuskobc76a6c2019-12-03 11:55:27 +0000133 auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
134 kTestMessagesDescriptor.size());
135 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
136 << status.message();
137
138 status = helper.InternProtoIntoArgsTable(
139 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
140 ".protozero.test.protos.EveryField",
141 TraceStorage::CreateRowId(TableId::kTrack, 0));
142
143 EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
144 << status.message();
145
146 context_.args_tracker->Flush();
147 EXPECT_TRUE(
148 HasArg(ArgSetId(arg_set_id_), "field_int32", Variadic::Integer(-1)));
149 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_int64",
150 Variadic::Integer(-333123456789ll)));
151 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_uint32",
152 Variadic::UnsignedInteger(600)));
153 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_uint64",
154 Variadic::UnsignedInteger(333123456789ll)));
155 EXPECT_TRUE(
156 HasArg(ArgSetId(arg_set_id_), "field_sint32", Variadic::Integer(-5)));
157 EXPECT_TRUE(
158 HasArg(ArgSetId(arg_set_id_), "field_sint64", Variadic::Integer(-9000)));
159 EXPECT_TRUE(
160 HasArg(ArgSetId(arg_set_id_), "field_fixed32", Variadic::Integer(12345)));
161 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_fixed64",
162 Variadic::Integer(444123450000ll)));
163 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_sfixed32",
164 Variadic::Integer(-69999)));
165 EXPECT_TRUE(
166 HasArg(ArgSetId(arg_set_id_), "field_sfixed64", Variadic::Integer(-200)));
167 EXPECT_TRUE(
168 HasArg(ArgSetId(arg_set_id_), "field_double", Variadic::Real(0.5555)));
169 EXPECT_TRUE(
170 HasArg(ArgSetId(arg_set_id_), "field_bool", Variadic::Boolean(true)));
171 EXPECT_TRUE(HasArg(
172 ArgSetId(arg_set_id_), "small_enum",
173 Variadic::String(*context_.storage->string_pool().GetId("TO_BE"))));
174 EXPECT_TRUE(HasArg(
175 ArgSetId(arg_set_id_), "signed_enum",
176 Variadic::String(*context_.storage->string_pool().GetId("NEGATIVE"))));
177 EXPECT_TRUE(HasArg(
178 ArgSetId(arg_set_id_), "big_enum",
179 Variadic::String(*context_.storage->string_pool().GetId("BEGIN"))));
180 EXPECT_TRUE(
181 HasArg(ArgSetId(arg_set_id_), "nested_enum",
182 Variadic::String(*context_.storage->string_pool().GetId("PONG"))));
183 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_float",
184 Variadic::Real(static_cast<double>(3.14f))));
185 ASSERT_TRUE(context_.storage->string_pool().GetId("FizzBuzz"));
186 EXPECT_TRUE(HasArg(
187 ArgSetId(arg_set_id_), "field_string",
188 Variadic::String(*context_.storage->string_pool().GetId("FizzBuzz"))));
189 EXPECT_TRUE(
190 HasArg(ArgSetId(arg_set_id_), "repeated_int32", Variadic::Integer(1)));
191 EXPECT_TRUE(
192 HasArg(ArgSetId(arg_set_id_), "repeated_int32", Variadic::Integer(-1)));
193}
194
195TEST_F(ArgsTableUtilsTest, NestedProto) {
196 using namespace protozero::test::protos::pbzero;
197 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
198 msg->set_super_nested()->set_value_c(3);
199
200 auto binary_proto = msg.SerializeAsArray();
201
202 storage_->mutable_track_table()->Insert({});
203 ProtoToArgsTable helper(sequence_state_.get(),
Stephen Nuskoe3c39952019-12-03 15:49:55 +0000204 sequence_state_->current_generation(), &context_,
205 nullptr, "", 0);
Stephen Nuskobc76a6c2019-12-03 11:55:27 +0000206 auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
207 kTestMessagesDescriptor.size());
208 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
209 << status.message();
210
211 status = helper.InternProtoIntoArgsTable(
212 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
213 ".protozero.test.protos.NestedA",
214 TraceStorage::CreateRowId(TableId::kTrack, 0));
215 EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
216 << status.message();
217 context_.args_tracker->Flush();
218 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "super_nested.value_c",
219 Variadic::Integer(3)));
220}
221
222TEST_F(ArgsTableUtilsTest, CamelCaseFieldsProto) {
223 using namespace protozero::test::protos::pbzero;
224 protozero::HeapBuffered<CamelCaseFields> msg{kChunkSize, kChunkSize};
225 msg->set_barbaz(true);
226 msg->set_moomoo(true);
227 msg->set___bigbang(true);
228
229 auto binary_proto = msg.SerializeAsArray();
230
231 storage_->mutable_track_table()->Insert({});
232 ProtoToArgsTable helper(sequence_state_.get(),
Stephen Nuskoe3c39952019-12-03 15:49:55 +0000233 sequence_state_->current_generation(), &context_,
234 nullptr, "", 0);
Stephen Nuskobc76a6c2019-12-03 11:55:27 +0000235 auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
236 kTestMessagesDescriptor.size());
237 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
238 << status.message();
239
240 status = helper.InternProtoIntoArgsTable(
241 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
242 ".protozero.test.protos.CamelCaseFields",
243 TraceStorage::CreateRowId(TableId::kTrack, 0));
244 EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
245 << status.message();
246 context_.args_tracker->Flush();
247 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "barBaz", Variadic::Boolean(true)));
248 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "MooMoo", Variadic::Boolean(true)));
249 EXPECT_TRUE(
250 HasArg(ArgSetId(arg_set_id_), "__bigBang", Variadic::Boolean(true)));
251}
252
253TEST_F(ArgsTableUtilsTest, NestedProtoParsingOverrideHandled) {
254 using namespace protozero::test::protos::pbzero;
255 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
256 msg->set_super_nested()->set_value_c(3);
257
258 auto binary_proto = msg.SerializeAsArray();
259
260 storage_->mutable_track_table()->Insert({});
261 ProtoToArgsTable helper(sequence_state_.get(),
Stephen Nuskoe3c39952019-12-03 15:49:55 +0000262 sequence_state_->current_generation(), &context_,
263 nullptr, "", 0);
Stephen Nuskobc76a6c2019-12-03 11:55:27 +0000264 auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
265 kTestMessagesDescriptor.size());
266 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
267 << status.message();
268
269 helper.AddParsingOverride(
270 "super_nested.value_c",
271 [](const ProtoToArgsTable::ParsingOverrideState& state,
272 const protozero::Field& field) {
273 EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
274 EXPECT_TRUE(state.args_tracker);
275 EXPECT_TRUE(state.context);
276 EXPECT_TRUE(state.sequence_state);
277 auto id = state.context->storage->InternString(
278 "super_nested.value_b.replaced");
279 int32_t val = field.as_int32();
280 state.args_tracker->AddArg(state.row_id, id, id,
281 Variadic::Integer(val + 1));
282 // We've handled this field by adding the desired args.
283 return true;
284 });
285 status = helper.InternProtoIntoArgsTable(
286 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
287 ".protozero.test.protos.NestedA",
288 TraceStorage::CreateRowId(TableId::kTrack, 0));
289 EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
290 << status.message();
291 context_.args_tracker->Flush();
292 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "super_nested.value_b.replaced",
293 Variadic::Integer(4)));
294}
295
296TEST_F(ArgsTableUtilsTest, NestedProtoParsingOverrideSkipped) {
297 using namespace protozero::test::protos::pbzero;
298 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
299 msg->set_super_nested()->set_value_c(3);
300
301 auto binary_proto = msg.SerializeAsArray();
302
303 storage_->mutable_track_table()->Insert({});
304 ProtoToArgsTable helper(sequence_state_.get(),
Stephen Nuskoe3c39952019-12-03 15:49:55 +0000305 sequence_state_->current_generation(), &context_,
306 nullptr, "", 0);
Stephen Nuskobc76a6c2019-12-03 11:55:27 +0000307 auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
308 kTestMessagesDescriptor.size());
309 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
310 << status.message();
311
312 helper.AddParsingOverride(
313 "super_nested.value_c",
314 [](const ProtoToArgsTable::ParsingOverrideState& state,
315 const protozero::Field& field) {
316 static int val = 0;
317 ++val;
318 EXPECT_EQ(1, val);
319 EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
320 EXPECT_TRUE(state.args_tracker);
321 EXPECT_TRUE(state.sequence_state);
322 EXPECT_TRUE(state.context);
323 // By returning false we expect this field to be handled like regular.
324 return false;
325 });
326 status = helper.InternProtoIntoArgsTable(
327 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
328 ".protozero.test.protos.NestedA",
329 TraceStorage::CreateRowId(TableId::kTrack, 0));
330 EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
331 << status.message();
332 context_.args_tracker->Flush();
333 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "super_nested.value_c",
334 Variadic::Integer(3)));
335}
336
337TEST_F(ArgsTableUtilsTest, LookingUpInternedStateParsingOverride) {
338 using namespace protozero::test::protos::pbzero;
339 // The test proto, we will use |value_c| as the source_location iid.
340 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
341 msg->set_super_nested()->set_value_c(3);
342 auto binary_proto = msg.SerializeAsArray();
343
344 // The interned source location.
345 protozero::HeapBuffered<protos::pbzero::SourceLocation> src_loc{kChunkSize,
346 kChunkSize};
347 src_loc->set_iid(3);
348 src_loc->set_file_name("test_file_name");
349 // We need to update sequence_state to point to it.
350 auto binary_data = src_loc.SerializeAsArray();
351 std::unique_ptr<uint8_t[]> buffer(new uint8_t[binary_data.size()]);
352 for (size_t i = 0; i < binary_data.size(); ++i) {
353 buffer.get()[i] = binary_data[i];
354 }
355 TraceBlobView blob(std::move(buffer), 0, binary_data.size());
356 sequence_state_->InternMessage(
357 protos::pbzero::InternedData::kSourceLocationsFieldNumber,
358 std::move(blob));
359
360 ProtoToArgsTable helper(sequence_state_.get(),
Stephen Nuskoe3c39952019-12-03 15:49:55 +0000361 sequence_state_->current_generation(), &context_,
362 nullptr, "", 0);
Stephen Nuskobc76a6c2019-12-03 11:55:27 +0000363 // Now we override the behaviour of |value_c| so we can expand the iid into
364 // multiple args rows.
365 helper.AddParsingOverride(
366 "super_nested.value_c",
367 [](const ProtoToArgsTable::ParsingOverrideState& state,
368 const protozero::Field& field) {
369 EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
370 auto* decoder = state.sequence_state->LookupInternedMessage<
371 protos::pbzero::InternedData::kSourceLocationsFieldNumber,
372 protos::pbzero::SourceLocation>(state.sequence_generation,
373 field.as_uint64());
374 if (!decoder) {
375 // Lookup failed fall back on default behaviour.
376 return false;
377 }
378 TraceStorage* storage = state.context->storage.get();
379 auto file_name_id = storage->InternString("file_name");
380 auto line_number_id = storage->InternString("line_number");
381 auto file_id = storage->InternString(decoder->file_name());
382 state.args_tracker->AddArg(state.row_id, file_name_id, file_name_id,
383 Variadic::String(file_id));
384 state.args_tracker->AddArg(state.row_id, line_number_id, line_number_id,
385 Variadic::Integer(2));
386 return true;
387 });
388
389 storage_->mutable_track_table()->Insert({});
390 auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
391 kTestMessagesDescriptor.size());
392 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
393 << status.message();
394
395 status = helper.InternProtoIntoArgsTable(
396 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
397 ".protozero.test.protos.NestedA",
398 TraceStorage::CreateRowId(TableId::kTrack, 0));
399 EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
400 << status.message();
401 auto file_name_id = storage_->string_pool().GetId("test_file_name");
402 ASSERT_TRUE(file_name_id);
403 context_.args_tracker->Flush();
404 EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "file_name",
405 Variadic::String(*file_name_id)));
406 EXPECT_TRUE(
407 HasArg(ArgSetId(arg_set_id_), "line_number", Variadic::Integer(2)));
408}
409
410} // namespace
411} // namespace trace_processor
412} // namespace perfetto