blob: e14efac0b923ee30c5c0a55989526bb1ec522620 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/importers/proto/args_table_utils.h"
#include "perfetto/ext/base/string_view.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "protos/perfetto/common/descriptor.pbzero.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/track_event/source_location.pbzero.h"
#include "src/protozero/test/example_proto/test_messages.descriptor.h"
#include "src/protozero/test/example_proto/test_messages.pbzero.h"
#include "src/trace_processor/args_tracker.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {
namespace trace_processor {
namespace {
constexpr size_t kChunkSize = 42;
using ::testing::_;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::NiceMock;
class ArgsTableUtilsTest : public ::testing::Test {
protected:
ArgsTableUtilsTest() {
context_.storage.reset(new TraceStorage);
storage_ = context_.storage.get();
context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
context_.args_tracker.reset(new ArgsTracker(&context_));
sequence_state_.reset(new PacketSequenceState(&context_));
}
/**
* Check whether the argument set contains the value with given flat_key and
* key and is equal to the given value.
*/
bool HasArg(ArgSetId set_id,
const base::StringView& flat_key,
const base::StringView& key,
Variadic value) {
const auto& args = storage_->arg_table();
auto key_id = storage_->string_pool().GetId(key);
EXPECT_TRUE(key_id);
auto flat_key_id = storage_->string_pool().GetId(flat_key);
EXPECT_TRUE(flat_key_id);
RowMap rm = args.FilterToRowMap({args.arg_set_id().eq(set_id)});
bool found = false;
for (auto it = rm.IterateRows(); it; it.Next()) {
if (args.key()[it.row()] == key_id) {
EXPECT_EQ(args.flat_key()[it.row()], flat_key_id);
if (storage_->GetArgValue(it.row()) == value) {
found = true;
break;
}
}
}
return found;
}
/**
* Implementation of HasArg for a simple case when flat_key is equals to key,
* so that two won't have to be repeated for each assertion.
*/
bool HasArg(ArgSetId set_id, const base::StringView& key, Variadic value) {
return HasArg(set_id, key, key, value);
}
uint32_t arg_set_id_ = 1;
std::unique_ptr<PacketSequenceState> sequence_state_;
TraceProcessorContext context_;
TraceStorage* storage_;
};
TEST_F(ArgsTableUtilsTest, EnsureTestMessageProtoParses) {
ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
0);
auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
kTestMessagesDescriptor.size());
EXPECT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
}
TEST_F(ArgsTableUtilsTest, BasicSingleLayerProto) {
using namespace protozero::test::protos::pbzero;
protozero::HeapBuffered<EveryField> msg{kChunkSize, kChunkSize};
msg->set_field_int32(-1);
msg->set_field_int64(-333123456789ll);
msg->set_field_uint32(600);
msg->set_field_uint64(333123456789ll);
msg->set_field_sint32(-5);
msg->set_field_sint64(-9000);
msg->set_field_fixed32(12345);
msg->set_field_fixed64(444123450000ll);
msg->set_field_sfixed32(-69999);
msg->set_field_sfixed64(-200);
msg->set_field_double(0.5555);
msg->set_field_bool(true);
msg->set_small_enum(SmallEnum::TO_BE);
msg->set_signed_enum(SignedEnum::NEGATIVE);
msg->set_big_enum(BigEnum::BEGIN);
msg->set_nested_enum(EveryField::PONG);
msg->set_field_float(3.14f);
msg->set_field_string("FizzBuzz");
msg->add_repeated_int32(1);
msg->add_repeated_int32(-1);
msg->add_repeated_int32(100);
msg->add_repeated_int32(2000000);
auto binary_proto = msg.SerializeAsArray();
storage_->mutable_track_table()->Insert({});
ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
0);
auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
kTestMessagesDescriptor.size());
ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
status = helper.InternProtoIntoArgsTable(
protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
".protozero.test.protos.EveryField", &inserter);
EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
<< status.message();
context_.args_tracker->Flush();
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "field_int32", Variadic::Integer(-1)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_int64",
Variadic::Integer(-333123456789ll)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_uint32",
Variadic::UnsignedInteger(600)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_uint64",
Variadic::UnsignedInteger(333123456789ll)));
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "field_sint32", Variadic::Integer(-5)));
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "field_sint64", Variadic::Integer(-9000)));
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "field_fixed32", Variadic::Integer(12345)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_fixed64",
Variadic::Integer(444123450000ll)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_sfixed32",
Variadic::Integer(-69999)));
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "field_sfixed64", Variadic::Integer(-200)));
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "field_double", Variadic::Real(0.5555)));
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "field_bool", Variadic::Boolean(true)));
EXPECT_TRUE(HasArg(
ArgSetId(arg_set_id_), "small_enum",
Variadic::String(*context_.storage->string_pool().GetId("TO_BE"))));
EXPECT_TRUE(HasArg(
ArgSetId(arg_set_id_), "signed_enum",
Variadic::String(*context_.storage->string_pool().GetId("NEGATIVE"))));
EXPECT_TRUE(HasArg(
ArgSetId(arg_set_id_), "big_enum",
Variadic::String(*context_.storage->string_pool().GetId("BEGIN"))));
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "nested_enum",
Variadic::String(*context_.storage->string_pool().GetId("PONG"))));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "field_float",
Variadic::Real(static_cast<double>(3.14f))));
ASSERT_TRUE(context_.storage->string_pool().GetId("FizzBuzz"));
EXPECT_TRUE(HasArg(
ArgSetId(arg_set_id_), "field_string",
Variadic::String(*context_.storage->string_pool().GetId("FizzBuzz"))));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "repeated_int32",
"repeated_int32[0]", Variadic::Integer(1)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "repeated_int32",
"repeated_int32[1]", Variadic::Integer(-1)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "repeated_int32",
"repeated_int32[2]", Variadic::Integer(100)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "repeated_int32",
"repeated_int32[3]", Variadic::Integer(2000000)));
}
TEST_F(ArgsTableUtilsTest, NestedProto) {
using namespace protozero::test::protos::pbzero;
protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
msg->set_super_nested()->set_value_c(3);
auto binary_proto = msg.SerializeAsArray();
storage_->mutable_track_table()->Insert({});
ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
0);
auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
kTestMessagesDescriptor.size());
ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
status = helper.InternProtoIntoArgsTable(
protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
".protozero.test.protos.NestedA", &inserter);
EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
<< status.message();
context_.args_tracker->Flush();
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "super_nested.value_c",
Variadic::Integer(3)));
}
TEST_F(ArgsTableUtilsTest, CamelCaseFieldsProto) {
using namespace protozero::test::protos::pbzero;
protozero::HeapBuffered<CamelCaseFields> msg{kChunkSize, kChunkSize};
msg->set_barbaz(true);
msg->set_moomoo(true);
msg->set___bigbang(true);
auto binary_proto = msg.SerializeAsArray();
storage_->mutable_track_table()->Insert({});
ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
0);
auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
kTestMessagesDescriptor.size());
ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
status = helper.InternProtoIntoArgsTable(
protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
".protozero.test.protos.CamelCaseFields", &inserter);
EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
<< status.message();
context_.args_tracker->Flush();
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "barBaz", Variadic::Boolean(true)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "MooMoo", Variadic::Boolean(true)));
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "__bigBang", Variadic::Boolean(true)));
}
TEST_F(ArgsTableUtilsTest, NestedProtoParsingOverrideHandled) {
using namespace protozero::test::protos::pbzero;
protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
msg->set_super_nested()->set_value_c(3);
auto binary_proto = msg.SerializeAsArray();
storage_->mutable_track_table()->Insert({});
ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
0);
auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
kTestMessagesDescriptor.size());
ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
helper.AddParsingOverride(
"super_nested.value_c",
[](const ProtoToArgsTable::ParsingOverrideState& state,
const protozero::Field& field, ArgsTracker::BoundInserter* inserter) {
EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
EXPECT_TRUE(state.context);
EXPECT_TRUE(state.sequence_state);
auto id = state.context->storage->InternString(
"super_nested.value_b.replaced");
int32_t val = field.as_int32();
inserter->AddArg(id, id, Variadic::Integer(val + 1));
// We've handled this field by adding the desired args.
return true;
});
auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
status = helper.InternProtoIntoArgsTable(
protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
".protozero.test.protos.NestedA", &inserter);
EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
<< status.message();
context_.args_tracker->Flush();
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "super_nested.value_b.replaced",
Variadic::Integer(4)));
}
TEST_F(ArgsTableUtilsTest, NestedProtoParsingOverrideSkipped) {
using namespace protozero::test::protos::pbzero;
protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
msg->set_super_nested()->set_value_c(3);
auto binary_proto = msg.SerializeAsArray();
storage_->mutable_track_table()->Insert({});
ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
0);
auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
kTestMessagesDescriptor.size());
ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
helper.AddParsingOverride(
"super_nested.value_c",
[](const ProtoToArgsTable::ParsingOverrideState& state,
const protozero::Field& field, ArgsTracker::BoundInserter*) {
static int val = 0;
++val;
EXPECT_EQ(1, val);
EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
EXPECT_TRUE(state.sequence_state);
EXPECT_TRUE(state.context);
// By returning false we expect this field to be handled like regular.
return false;
});
auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
status = helper.InternProtoIntoArgsTable(
protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
".protozero.test.protos.NestedA", &inserter);
EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
<< status.message();
context_.args_tracker->Flush();
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "super_nested.value_c",
Variadic::Integer(3)));
}
TEST_F(ArgsTableUtilsTest, LookingUpInternedStateParsingOverride) {
using namespace protozero::test::protos::pbzero;
// The test proto, we will use |value_c| as the source_location iid.
protozero::HeapBuffered<NestedA> msg{kChunkSize, kChunkSize};
msg->set_super_nested()->set_value_c(3);
auto binary_proto = msg.SerializeAsArray();
// The interned source location.
protozero::HeapBuffered<protos::pbzero::SourceLocation> src_loc{kChunkSize,
kChunkSize};
src_loc->set_iid(3);
src_loc->set_file_name("test_file_name");
// We need to update sequence_state to point to it.
auto binary_data = src_loc.SerializeAsArray();
std::unique_ptr<uint8_t[]> buffer(new uint8_t[binary_data.size()]);
for (size_t i = 0; i < binary_data.size(); ++i) {
buffer.get()[i] = binary_data[i];
}
TraceBlobView blob(std::move(buffer), 0, binary_data.size());
sequence_state_->InternMessage(
protos::pbzero::InternedData::kSourceLocationsFieldNumber,
std::move(blob));
ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
0);
// Now we override the behaviour of |value_c| so we can expand the iid into
// multiple args rows.
helper.AddParsingOverride(
"super_nested.value_c",
[](const ProtoToArgsTable::ParsingOverrideState& state,
const protozero::Field& field, ArgsTracker::BoundInserter* inserter) {
EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
auto* decoder = state.sequence_state->LookupInternedMessage<
protos::pbzero::InternedData::kSourceLocationsFieldNumber,
protos::pbzero::SourceLocation>(field.as_uint64());
if (!decoder) {
// Lookup failed fall back on default behaviour.
return false;
}
TraceStorage* storage = state.context->storage.get();
auto file_name_id = storage->InternString("file_name");
auto line_number_id = storage->InternString("line_number");
auto file_id = storage->InternString(decoder->file_name());
inserter->AddArg(file_name_id, file_name_id, Variadic::String(file_id));
inserter->AddArg(line_number_id, line_number_id, Variadic::Integer(2));
return true;
});
storage_->mutable_track_table()->Insert({});
auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
kTestMessagesDescriptor.size());
ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
status = helper.InternProtoIntoArgsTable(
protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
".protozero.test.protos.NestedA", &inserter);
EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
<< status.message();
auto file_name_id = storage_->string_pool().GetId("test_file_name");
ASSERT_TRUE(file_name_id);
context_.args_tracker->Flush();
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "file_name",
Variadic::String(*file_name_id)));
EXPECT_TRUE(
HasArg(ArgSetId(arg_set_id_), "line_number", Variadic::Integer(2)));
}
TEST_F(ArgsTableUtilsTest, NonEmptyPrefix) {
using namespace protozero::test::protos::pbzero;
protozero::HeapBuffered<EveryField> msg{kChunkSize, kChunkSize};
msg->add_repeated_int32(1);
msg->add_repeated_int32(-1);
msg->add_repeated_int32(100);
msg->add_repeated_int32(2000000);
auto binary_proto = msg.SerializeAsArray();
storage_->mutable_track_table()->Insert({});
ProtoToArgsTable helper(sequence_state_->current_generation(), &context_,
"prefix", 0);
auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
kTestMessagesDescriptor.size());
ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
<< status.message();
auto inserter = context_.args_tracker->AddArgsTo(TrackId(0));
status = helper.InternProtoIntoArgsTable(
protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
".protozero.test.protos.EveryField", &inserter);
EXPECT_TRUE(status.ok()) << "InternProtoIntoArgsTable failed with error: "
<< status.message();
context_.args_tracker->Flush();
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "prefix.repeated_int32",
"prefix.repeated_int32[0]", Variadic::Integer(1)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "prefix.repeated_int32",
"prefix.repeated_int32[1]", Variadic::Integer(-1)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "prefix.repeated_int32",
"prefix.repeated_int32[2]", Variadic::Integer(100)));
EXPECT_TRUE(HasArg(ArgSetId(arg_set_id_), "prefix.repeated_int32",
"prefix.repeated_int32[3]", Variadic::Integer(2000000)));
}
} // namespace
} // namespace trace_processor
} // namespace perfetto