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/proto_to_args_parser.h"
18
19 #include <array>
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstring>
23 #include <ios>
24 #include <limits>
25 #include <map>
26 #include <memory>
27 #include <optional>
28 #include <sstream>
29 #include <string>
30 #include <utility>
31 #include <vector>
32
33 #include "perfetto/base/status.h"
34 #include "perfetto/protozero/field.h"
35 #include "perfetto/protozero/packed_repeated_fields.h"
36 #include "perfetto/protozero/proto_utils.h"
37 #include "perfetto/protozero/scattered_heap_buffer.h"
38 #include "perfetto/trace_processor/trace_blob.h"
39 #include "perfetto/trace_processor/trace_blob_view.h"
40 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
41 #include "src/trace_processor/test_messages.descriptor.h"
42 #include "src/trace_processor/util/descriptors.h"
43 #include "src/trace_processor/util/interned_message_view.h"
44 #include "test/gtest_and_gmock.h"
45
46 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
47 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
48
49 namespace perfetto::trace_processor::util {
50 namespace {
51
52 constexpr size_t kChunkSize = 42;
53
ToChars(const char * str)54 protozero::ConstChars ToChars(const char* str) {
55 return protozero::ConstChars{str, strlen(str)};
56 }
57
58 class ProtoToArgsParserTest : public ::testing::Test,
59 public ProtoToArgsParser::Delegate {
60 protected:
ProtoToArgsParserTest()61 ProtoToArgsParserTest() {}
62
args() const63 const std::vector<std::string>& args() const { return args_; }
64
AddInternedSourceLocation(uint64_t iid,TraceBlobView data)65 void AddInternedSourceLocation(uint64_t iid, TraceBlobView data) {
66 interned_source_locations_[iid] = std::unique_ptr<InternedMessageView>(
67 new InternedMessageView(std::move(data)));
68 }
69
70 template <typename T, typename... Ts>
CreatedPackedVarint(protozero::PackedVarInt & var,T p,Ts...ps)71 void CreatedPackedVarint(protozero::PackedVarInt& var, T p, Ts... ps) {
72 var.Reset();
73 std::array<T, sizeof...(ps) + 1> list = {p, ps...};
74 for (T v : list) {
75 var.Append(v);
76 }
77 }
78
79 private:
80 using Key = ProtoToArgsParser::Key;
81
AddInteger(const Key & key,int64_t value)82 void AddInteger(const Key& key, int64_t value) override {
83 std::stringstream ss;
84 ss << key.flat_key << " " << key.key << " " << value;
85 args_.push_back(ss.str());
86 }
87
AddUnsignedInteger(const Key & key,uint64_t value)88 void AddUnsignedInteger(const Key& key, uint64_t value) override {
89 std::stringstream ss;
90 ss << key.flat_key << " " << key.key << " " << value;
91 args_.push_back(ss.str());
92 }
93
AddString(const Key & key,const protozero::ConstChars & value)94 void AddString(const Key& key, const protozero::ConstChars& value) override {
95 std::stringstream ss;
96 ss << key.flat_key << " " << key.key << " " << value.ToStdString();
97 args_.push_back(ss.str());
98 }
99
AddString(const Key & key,const std::string & value)100 void AddString(const Key& key, const std::string& value) override {
101 std::stringstream ss;
102 ss << key.flat_key << " " << key.key << " " << value;
103 args_.push_back(ss.str());
104 }
105
AddBytes(const Key & key,const protozero::ConstBytes & value)106 void AddBytes(const Key& key, const protozero::ConstBytes& value) override {
107 std::stringstream ss;
108 ss << key.flat_key << " " << key.key << " <bytes size=" << value.size
109 << ">";
110 args_.push_back(ss.str());
111 }
112
AddDouble(const Key & key,double value)113 void AddDouble(const Key& key, double value) override {
114 std::stringstream ss;
115 ss << key.flat_key << " " << key.key << " " << value;
116 args_.push_back(ss.str());
117 }
118
AddPointer(const Key & key,uint64_t value)119 void AddPointer(const Key& key, uint64_t value) override {
120 std::stringstream ss;
121 ss << key.flat_key << " " << key.key << " " << std::hex << value
122 << std::dec;
123 args_.push_back(ss.str());
124 }
125
AddBoolean(const Key & key,bool value)126 void AddBoolean(const Key& key, bool value) override {
127 std::stringstream ss;
128 ss << key.flat_key << " " << key.key << " " << (value ? "true" : "false");
129 args_.push_back(ss.str());
130 }
131
AddJson(const Key & key,const protozero::ConstChars & value)132 bool AddJson(const Key& key, const protozero::ConstChars& value) override {
133 std::stringstream ss;
134 ss << key.flat_key << " " << key.key << " " << std::hex
135 << value.ToStdString() << std::dec;
136 args_.push_back(ss.str());
137 return true;
138 }
139
AddNull(const Key & key)140 void AddNull(const Key& key) override {
141 std::stringstream ss;
142 ss << key.flat_key << " " << key.key << " [NULL]";
143 args_.push_back(ss.str());
144 }
145
GetArrayEntryIndex(const std::string &)146 size_t GetArrayEntryIndex(const std::string&) final { return 0; }
147
IncrementArrayEntryIndex(const std::string &)148 size_t IncrementArrayEntryIndex(const std::string&) final { return 0; }
149
GetInternedMessageView(uint32_t field_id,uint64_t iid)150 InternedMessageView* GetInternedMessageView(uint32_t field_id,
151 uint64_t iid) override {
152 if (field_id != protos::pbzero::InternedData::kSourceLocationsFieldNumber)
153 return nullptr;
154 return interned_source_locations_.at(iid).get();
155 }
156
seq_state()157 PacketSequenceStateGeneration* seq_state() final { return nullptr; }
158
159 std::vector<std::string> args_;
160 std::map<uint64_t, std::unique_ptr<InternedMessageView>>
161 interned_source_locations_;
162 };
163
TEST_F(ProtoToArgsParserTest,EnsureTestMessageProtoParses)164 TEST_F(ProtoToArgsParserTest, EnsureTestMessageProtoParses) {
165 DescriptorPool pool;
166 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
167 kTestMessagesDescriptor.size());
168 ProtoToArgsParser parser(pool);
169 EXPECT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
170 << status.message();
171 }
172
TEST_F(ProtoToArgsParserTest,BasicSingleLayerProto)173 TEST_F(ProtoToArgsParserTest, BasicSingleLayerProto) {
174 using namespace protozero::test::protos::pbzero;
175 protozero::HeapBuffered<EveryField> msg{kChunkSize, kChunkSize};
176 msg->set_field_int32(-1);
177 msg->set_field_int64(-333123456789ll);
178 msg->set_field_uint32(600);
179 msg->set_field_uint64(333123456789ll);
180 msg->set_field_sint32(-5);
181 msg->set_field_sint64(-9000);
182 msg->set_field_fixed32(12345);
183 msg->set_field_fixed64(444123450000ll);
184 msg->set_field_sfixed32(-69999);
185 msg->set_field_sfixed64(-200);
186 msg->set_field_double(0.5555);
187 msg->set_field_bool(true);
188 msg->set_small_enum(SmallEnum::TO_BE);
189 msg->set_signed_enum(SignedEnum::NEGATIVE);
190 msg->set_big_enum(BigEnum::BEGIN);
191 msg->set_nested_enum(EveryField::PONG);
192 msg->set_field_float(3.14f);
193 msg->set_field_string("FizzBuzz");
194 msg->add_repeated_int32(1);
195 msg->add_repeated_int32(-1);
196 msg->add_repeated_int32(100);
197 msg->add_repeated_int32(2000000);
198 msg->set_field_bytes({0, 1, 2});
199
200 auto binary_proto = msg.SerializeAsArray();
201
202 DescriptorPool pool;
203 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
204 kTestMessagesDescriptor.size());
205 ProtoToArgsParser parser(pool);
206 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
207 << status.message();
208
209 status = parser.ParseMessage(
210 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
211 ".protozero.test.protos.EveryField", nullptr, *this);
212
213 EXPECT_TRUE(status.ok())
214 << "InternProtoFieldsIntoArgsTable failed with error: "
215 << status.message();
216
217 EXPECT_THAT(
218 args(),
219 testing::ElementsAre(
220 "field_int32 field_int32 -1", "field_int64 field_int64 -333123456789",
221 "field_uint32 field_uint32 600",
222 "field_uint64 field_uint64 333123456789",
223 "field_sint32 field_sint32 -5", "field_sint64 field_sint64 -9000",
224 "field_fixed32 field_fixed32 12345",
225 "field_fixed64 field_fixed64 444123450000",
226 "field_sfixed32 field_sfixed32 -69999",
227 "field_sfixed64 field_sfixed64 -200",
228 "field_double field_double 0.5555", "field_bool field_bool true",
229 "small_enum small_enum TO_BE", "signed_enum signed_enum NEGATIVE",
230 "big_enum big_enum BEGIN", "nested_enum nested_enum PONG",
231 "field_float field_float 3.14", "field_string field_string FizzBuzz",
232 "repeated_int32 repeated_int32[0] 1",
233 "repeated_int32 repeated_int32[1] -1",
234 "repeated_int32 repeated_int32[2] 100",
235 "repeated_int32 repeated_int32[3] 2000000",
236 "field_bytes field_bytes <bytes size=3>"));
237 }
238
TEST_F(ProtoToArgsParserTest,NestedProto)239 TEST_F(ProtoToArgsParserTest, NestedProto) {
240 using namespace protozero::test::protos::pbzero;
241 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
242 msg->set_super_nested()->set_value_c(3);
243
244 auto binary_proto = msg.SerializeAsArray();
245
246 DescriptorPool pool;
247 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
248 kTestMessagesDescriptor.size());
249 ProtoToArgsParser parser(pool);
250 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
251 << status.message();
252
253 status = parser.ParseMessage(
254 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
255 ".protozero.test.protos.NestedA", nullptr, *this);
256 EXPECT_TRUE(status.ok())
257 << "InternProtoFieldsIntoArgsTable failed with error: "
258 << status.message();
259 EXPECT_THAT(args(), testing::ElementsAre(
260 "super_nested.value_c super_nested.value_c 3"));
261 }
262
TEST_F(ProtoToArgsParserTest,CamelCaseFieldsProto)263 TEST_F(ProtoToArgsParserTest, CamelCaseFieldsProto) {
264 using namespace protozero::test::protos::pbzero;
265 protozero::HeapBuffered<CamelCaseFields> msg{kChunkSize, kChunkSize};
266 msg->set_barbaz(true);
267 msg->set_moomoo(true);
268 msg->set___bigbang(true);
269
270 auto binary_proto = msg.SerializeAsArray();
271
272 DescriptorPool pool;
273 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
274 kTestMessagesDescriptor.size());
275 ProtoToArgsParser parser(pool);
276 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
277 << status.message();
278
279 status = parser.ParseMessage(
280 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
281 ".protozero.test.protos.CamelCaseFields", nullptr, *this);
282 EXPECT_TRUE(status.ok())
283 << "InternProtoFieldsIntoArgsTable failed with error: "
284 << status.message();
285 EXPECT_THAT(args(),
286 testing::ElementsAre("barBaz barBaz true", "MooMoo MooMoo true",
287 "__bigBang __bigBang true"));
288 }
289
TEST_F(ProtoToArgsParserTest,NestedProtoParsingOverrideHandled)290 TEST_F(ProtoToArgsParserTest, NestedProtoParsingOverrideHandled) {
291 using namespace protozero::test::protos::pbzero;
292 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
293 msg->set_super_nested()->set_value_c(3);
294
295 auto binary_proto = msg.SerializeAsArray();
296
297 DescriptorPool pool;
298 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
299 kTestMessagesDescriptor.size());
300 ProtoToArgsParser parser(pool);
301 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
302 << status.message();
303
304 parser.AddParsingOverrideForField(
305 "super_nested.value_c",
306 [](const protozero::Field& field, ProtoToArgsParser::Delegate& writer) {
307 EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
308 std::string key = "super_nested.value_b.replaced";
309 writer.AddInteger({key, key}, field.as_int32());
310 // We've handled this field by adding the desired args.
311 return base::OkStatus();
312 });
313
314 status = parser.ParseMessage(
315 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
316 ".protozero.test.protos.NestedA", nullptr, *this);
317 EXPECT_TRUE(status.ok())
318 << "InternProtoFieldsIntoArgsTable failed with error: "
319 << status.message();
320 EXPECT_THAT(
321 args(),
322 testing::ElementsAre(
323 "super_nested.value_b.replaced super_nested.value_b.replaced 3"));
324 }
325
TEST_F(ProtoToArgsParserTest,NestedProtoParsingOverrideSkipped)326 TEST_F(ProtoToArgsParserTest, NestedProtoParsingOverrideSkipped) {
327 using namespace protozero::test::protos::pbzero;
328 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
329 msg->set_super_nested()->set_value_c(3);
330
331 auto binary_proto = msg.SerializeAsArray();
332
333 DescriptorPool pool;
334 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
335 kTestMessagesDescriptor.size());
336 ProtoToArgsParser parser(pool);
337 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
338 << status.message();
339
340 parser.AddParsingOverrideForField(
341 "super_nested.value_c",
342 [](const protozero::Field& field, ProtoToArgsParser::Delegate&) {
343 static int val = 0;
344 ++val;
345 EXPECT_EQ(1, val);
346 EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
347 return std::nullopt;
348 });
349
350 status = parser.ParseMessage(
351 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
352 ".protozero.test.protos.NestedA", nullptr, *this);
353 EXPECT_TRUE(status.ok())
354 << "InternProtoFieldsIntoArgsTable failed with error: "
355 << status.message();
356 EXPECT_THAT(args(), testing::ElementsAre(
357 "super_nested.value_c super_nested.value_c 3"));
358 }
359
TEST_F(ProtoToArgsParserTest,LookingUpInternedStateParsingOverride)360 TEST_F(ProtoToArgsParserTest, LookingUpInternedStateParsingOverride) {
361 using namespace protozero::test::protos::pbzero;
362 // The test proto, we will use |value_c| as the source_location iid.
363 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
364 msg->set_super_nested()->set_value_c(3);
365 auto binary_proto = msg.SerializeAsArray();
366
367 // The interned source location.
368 protozero::HeapBuffered<protos::pbzero::SourceLocation> src_loc{kChunkSize,
369 kChunkSize};
370 const uint64_t kIid = 3;
371 src_loc->set_iid(kIid);
372 src_loc->set_file_name("test_file_name");
373 // We need to update sequence_state to point to it.
374 auto binary_data = src_loc.SerializeAsArray();
375 std::unique_ptr<uint8_t[]> buffer(new uint8_t[binary_data.size()]);
376 for (size_t i = 0; i < binary_data.size(); ++i) {
377 buffer.get()[i] = binary_data[i];
378 }
379 TraceBlob blob =
380 TraceBlob::TakeOwnership(std::move(buffer), binary_data.size());
381 AddInternedSourceLocation(kIid, TraceBlobView(std::move(blob)));
382
383 DescriptorPool pool;
384 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
385 kTestMessagesDescriptor.size());
386 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
387 << status.message();
388
389 ProtoToArgsParser parser(pool);
390 // Now we override the behaviour of |value_c| so we can expand the iid into
391 // multiple args rows.
392 parser.AddParsingOverrideForField(
393 "super_nested.value_c",
394 [](const protozero::Field& field,
395 ProtoToArgsParser::Delegate& delegate) -> std::optional<base::Status> {
396 auto* decoder = delegate.GetInternedMessage(
397 protos::pbzero::InternedData::kSourceLocations, field.as_uint64());
398 if (!decoder) {
399 // Lookup failed fall back on default behaviour.
400 return std::nullopt;
401 }
402 delegate.AddString(ProtoToArgsParser::Key("file_name"),
403 protozero::ConstChars{"file", 4});
404 delegate.AddInteger(ProtoToArgsParser::Key("line_number"), 2);
405 return base::OkStatus();
406 });
407
408 status = parser.ParseMessage(
409 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
410 ".protozero.test.protos.NestedA", nullptr, *this);
411 EXPECT_TRUE(status.ok())
412 << "InternProtoFieldsIntoArgsTable failed with error: "
413 << status.message();
414 EXPECT_THAT(args(), testing::ElementsAre("file_name file_name file",
415 "line_number line_number 2"));
416 }
417
TEST_F(ProtoToArgsParserTest,OverrideForType)418 TEST_F(ProtoToArgsParserTest, OverrideForType) {
419 using namespace protozero::test::protos::pbzero;
420 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
421 msg->set_super_nested()->set_value_c(3);
422
423 auto binary_proto = msg.SerializeAsArray();
424
425 DescriptorPool pool;
426 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
427 kTestMessagesDescriptor.size());
428 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
429 << status.message();
430
431 ProtoToArgsParser parser(pool);
432
433 parser.AddParsingOverrideForType(
434 ".protozero.test.protos.NestedA.NestedB.NestedC",
435 [](ProtoToArgsParser::ScopedNestedKeyContext&,
436 const protozero::ConstBytes&, Delegate& delegate) {
437 delegate.AddInteger(ProtoToArgsParser::Key("arg"), 42);
438 return base::OkStatus();
439 });
440
441 status = parser.ParseMessage(
442 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
443 ".protozero.test.protos.NestedA", nullptr, *this);
444 EXPECT_TRUE(status.ok())
445 << "InternProtoFieldsIntoArgsTable failed with error: "
446 << status.message();
447 EXPECT_THAT(args(), testing::ElementsAre("arg arg 42"));
448 }
449
TEST_F(ProtoToArgsParserTest,FieldOverrideTakesPrecedence)450 TEST_F(ProtoToArgsParserTest, FieldOverrideTakesPrecedence) {
451 using namespace protozero::test::protos::pbzero;
452 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
453 msg->set_super_nested()->set_value_c(3);
454
455 auto binary_proto = msg.SerializeAsArray();
456
457 DescriptorPool pool;
458 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
459 kTestMessagesDescriptor.size());
460 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
461 << status.message();
462
463 ProtoToArgsParser parser(pool);
464
465 parser.AddParsingOverrideForField(
466 "super_nested",
467 [](const protozero::Field&, ProtoToArgsParser::Delegate& writer) {
468 writer.AddString(ProtoToArgsParser::Key("arg"),
469 ToChars("override-for-field"));
470 return base::OkStatus();
471 });
472
473 parser.AddParsingOverrideForType(
474 ".protozero.test.protos.NestedA.NestedB.NestedC",
475 [](ProtoToArgsParser::ScopedNestedKeyContext&,
476 const protozero::ConstBytes&, Delegate& delegate) {
477 delegate.AddString(ProtoToArgsParser::Key("arg"),
478 ToChars("override-for-type"));
479 return base::OkStatus();
480 });
481
482 status = parser.ParseMessage(
483 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
484 ".protozero.test.protos.NestedA", nullptr, *this);
485 EXPECT_TRUE(status.ok())
486 << "InternProtoFieldsIntoArgsTable failed with error: "
487 << status.message();
488 EXPECT_THAT(args(), testing::ElementsAre("arg arg override-for-field"));
489 }
490
TEST_F(ProtoToArgsParserTest,EmptyMessage)491 TEST_F(ProtoToArgsParserTest, EmptyMessage) {
492 using namespace protozero::test::protos::pbzero;
493 protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
494 msg->set_super_nested();
495
496 auto binary_proto = msg.SerializeAsArray();
497
498 DescriptorPool pool;
499 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
500 kTestMessagesDescriptor.size());
501 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
502 << status.message();
503
504 ProtoToArgsParser parser(pool);
505 status = parser.ParseMessage(
506 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
507 ".protozero.test.protos.NestedA", nullptr, *this);
508 EXPECT_TRUE(status.ok())
509 << "InternProtoFieldsIntoArgsTable failed with error: "
510 << status.message();
511 EXPECT_THAT(args(), testing::ElementsAre("super_nested super_nested [NULL]"));
512 }
513
TEST_F(ProtoToArgsParserTest,WidthAndSignednessOfScalars)514 TEST_F(ProtoToArgsParserTest, WidthAndSignednessOfScalars) {
515 using namespace protozero::test::protos::pbzero;
516 protozero::HeapBuffered<EveryField> msg{kChunkSize, kChunkSize};
517
518 // Set fields to values with the top bit set, and check that the parser
519 // retains the full value with the correct sign.
520 msg->set_field_int32(-0x80000000ll);
521 msg->set_field_sint32(-0x80000000ll);
522 msg->set_field_sfixed32(-0x80000000ll);
523
524 msg->set_field_uint32(0x80000000ull);
525 msg->set_field_fixed32(0x80000000ull);
526
527 msg->set_field_int64(-0x7FFFFFFFFFFFFFFFll - 1);
528 msg->set_field_sint64(-0x7FFFFFFFFFFFFFFFll - 1);
529 msg->set_field_sfixed64(-0x7FFFFFFFFFFFFFFFll - 1);
530
531 msg->set_field_uint64(0x8000000000000000ull);
532 msg->set_field_fixed64(0x8000000000000000ull);
533
534 auto binary_proto = msg.SerializeAsArray();
535
536 DescriptorPool pool;
537 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
538 kTestMessagesDescriptor.size());
539 ProtoToArgsParser parser(pool);
540 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
541 << status.message();
542
543 status = parser.ParseMessage(
544 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
545 ".protozero.test.protos.EveryField", nullptr, *this);
546
547 EXPECT_TRUE(status.ok())
548 << "InternProtoFieldsIntoArgsTable failed with error: "
549 << status.message();
550
551 EXPECT_THAT(args(), testing::ElementsAre(
552 "field_int32 field_int32 -2147483648",
553 "field_sint32 field_sint32 -2147483648",
554 "field_sfixed32 field_sfixed32 -2147483648",
555 "field_uint32 field_uint32 2147483648",
556 "field_fixed32 field_fixed32 2147483648",
557 "field_int64 field_int64 -9223372036854775808",
558 "field_sint64 field_sint64 -9223372036854775808",
559 "field_sfixed64 field_sfixed64 -9223372036854775808",
560 "field_uint64 field_uint64 9223372036854775808",
561 "field_fixed64 field_fixed64 9223372036854775808"));
562 }
563
TEST_F(ProtoToArgsParserTest,PackedFields)564 TEST_F(ProtoToArgsParserTest, PackedFields) {
565 using namespace protozero::test::protos::pbzero;
566 protozero::HeapBuffered<PackedRepeatedFields> msg{kChunkSize, kChunkSize};
567
568 protozero::PackedVarInt varint;
569 CreatedPackedVarint(varint, 0, std::numeric_limits<int32_t>::min(),
570 std::numeric_limits<int32_t>::max());
571 msg->set_field_int32(varint);
572
573 CreatedPackedVarint(varint, 0ll, std::numeric_limits<int64_t>::min(),
574 std::numeric_limits<int64_t>::max());
575 msg->set_field_int64(varint);
576
577 CreatedPackedVarint(varint, 0u, std::numeric_limits<uint32_t>::min(),
578 std::numeric_limits<uint32_t>::max());
579 msg->set_field_uint32(varint);
580
581 CreatedPackedVarint(varint, 0ull, std::numeric_limits<uint64_t>::min(),
582 std::numeric_limits<uint64_t>::max());
583 msg->set_field_uint64(varint);
584
585 CreatedPackedVarint(varint, BigEnum::BEGIN, BigEnum::END);
586 msg->set_big_enum(varint);
587
588 protozero::PackedFixedSizeInt<uint32_t> fixed32;
589 fixed32.Append(0);
590 fixed32.Append(std::numeric_limits<uint32_t>::min());
591 fixed32.Append(std::numeric_limits<uint32_t>::max());
592 msg->set_field_fixed32(fixed32);
593
594 protozero::PackedFixedSizeInt<int32_t> sfixed32;
595 sfixed32.Append(0);
596 sfixed32.Append(std::numeric_limits<int32_t>::min());
597 sfixed32.Append(std::numeric_limits<int32_t>::max());
598 msg->set_field_sfixed32(sfixed32);
599
600 protozero::PackedFixedSizeInt<float> pfloat;
601 pfloat.Append(0);
602 pfloat.Append(-4839.349f);
603 pfloat.Append(std::numeric_limits<float>::min());
604 pfloat.Append(std::numeric_limits<float>::max());
605 msg->set_field_float(pfloat);
606
607 protozero::PackedFixedSizeInt<uint64_t> fixed64;
608 fixed64.Append(0);
609 fixed64.Append(std::numeric_limits<uint64_t>::min());
610 fixed64.Append(std::numeric_limits<uint64_t>::max());
611 msg->set_field_fixed64(fixed64);
612
613 protozero::PackedFixedSizeInt<int64_t> sfixed64;
614 sfixed64.Append(0);
615 sfixed64.Append(std::numeric_limits<int64_t>::min());
616 sfixed64.Append(std::numeric_limits<int64_t>::max());
617 msg->set_field_sfixed64(sfixed64);
618
619 protozero::PackedFixedSizeInt<double> pdouble;
620 pdouble.Append(0);
621 pdouble.Append(-48948908.349);
622 pdouble.Append(std::numeric_limits<double>::min());
623 pdouble.Append(std::numeric_limits<double>::max());
624 msg->set_field_double(pdouble);
625
626 auto binary_proto = msg.SerializeAsArray();
627
628 DescriptorPool pool;
629 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
630 kTestMessagesDescriptor.size());
631 ProtoToArgsParser parser(pool);
632 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
633 << status.message();
634
635 status = parser.ParseMessage(
636 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
637 ".protozero.test.protos.PackedRepeatedFields", nullptr, *this);
638
639 EXPECT_TRUE(status.ok()) << "ParseMessage failed with error: "
640 << status.message();
641
642 EXPECT_THAT(
643 args(),
644 testing::ElementsAre(
645 "field_int32 field_int32[0] 0",
646 "field_int32 field_int32[1] -2147483648",
647 "field_int32 field_int32[2] 2147483647",
648 "field_int64 field_int64[0] 0",
649 "field_int64 field_int64[1] -9223372036854775808",
650 "field_int64 field_int64[2] 9223372036854775807",
651 "field_uint32 field_uint32[0] 0", "field_uint32 field_uint32[1] 0",
652 "field_uint32 field_uint32[2] 4294967295",
653 "field_uint64 field_uint64[0] 0", "field_uint64 field_uint64[1] 0",
654 "field_uint64 field_uint64[2] 18446744073709551615",
655 "big_enum big_enum[0] BEGIN", "big_enum big_enum[1] END",
656 "field_fixed32 field_fixed32[0] 0",
657 "field_fixed32 field_fixed32[1] 0",
658 "field_fixed32 field_fixed32[2] 4294967295",
659 "field_sfixed32 field_sfixed32[0] 0",
660 "field_sfixed32 field_sfixed32[1] -2147483648",
661 "field_sfixed32 field_sfixed32[2] 2147483647",
662 "field_float field_float[0] 0", "field_float field_float[1] -4839.35",
663 "field_float field_float[2] 1.17549e-38",
664 "field_float field_float[3] 3.40282e+38",
665 "field_fixed64 field_fixed64[0] 0",
666 "field_fixed64 field_fixed64[1] 0",
667 "field_fixed64 field_fixed64[2] 18446744073709551615",
668 "field_sfixed64 field_sfixed64[0] 0",
669 "field_sfixed64 field_sfixed64[1] -9223372036854775808",
670 "field_sfixed64 field_sfixed64[2] 9223372036854775807",
671 "field_double field_double[0] 0",
672 "field_double field_double[1] -4.89489e+07",
673 "field_double field_double[2] 2.22507e-308",
674 "field_double field_double[3] 1.79769e+308"));
675 }
676
TEST_F(ProtoToArgsParserTest,AddsDefaults)677 TEST_F(ProtoToArgsParserTest, AddsDefaults) {
678 using namespace protozero::test::protos::pbzero;
679 protozero::HeapBuffered<EveryField> msg{kChunkSize, kChunkSize};
680 msg->set_field_int32(-1);
681 msg->add_repeated_string("test");
682 msg->add_repeated_sfixed32(1);
683 msg->add_repeated_fixed64(1);
684 msg->set_nested_enum(EveryField::PONG);
685
686 auto binary_proto = msg.SerializeAsArray();
687
688 DescriptorPool pool;
689 auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
690 kTestMessagesDescriptor.size());
691 ProtoToArgsParser parser(pool);
692 ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
693 << status.message();
694
695 status = parser.ParseMessage(
696 protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
697 ".protozero.test.protos.EveryField", nullptr, *this, nullptr, true);
698
699 EXPECT_TRUE(status.ok()) << "AddsDefaults failed with error: "
700 << status.message();
701
702 EXPECT_THAT(
703 args(),
704 testing::UnorderedElementsAre(
705 "field_int32 field_int32 -1", // exists in message
706 "repeated_string repeated_string[0] test",
707 "repeated_sfixed32 repeated_sfixed32[0] 1",
708 "repeated_fixed64 repeated_fixed64[0] 1",
709 "nested_enum nested_enum PONG",
710 "field_bytes field_bytes <bytes size=0>",
711 "field_string field_string [NULL]", // null if no string default
712 "field_nested field_nested [NULL]", // no defaults for inner fields
713 "field_bool field_bool false",
714 "repeated_int32 repeated_int32 [NULL]", // null for repeated fields
715 "field_double field_double 0", "field_float field_float 0",
716 "field_sfixed64 field_sfixed64 0", "field_sfixed32 field_sfixed32 0",
717 "field_fixed64 field_fixed64 0", "field_sint64 field_sint64 0",
718 "big_enum big_enum 0", "field_fixed32 field_fixed32 0",
719 "field_sint32 field_sint32 0",
720 "signed_enum signed_enum NEUTRAL", // translates default enum
721 "small_enum small_enum NOT_TO_BE",
722 "very_negative_enum very_negative_enum DEF",
723 "field_uint64 field_uint64 0", "field_uint32 field_uint32 0",
724 "field_int64 field_int64 0"));
725 }
726
727 } // namespace
728 } // namespace perfetto::trace_processor::util
729