| /* | |
| * Copyright (C) 2023 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 <atomic> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <string_view> | |
| #include <thread> | |
| #include <vector> | |
| #include "perfetto/base/time.h" | |
| #include "perfetto/ext/base/flags.h" | |
| #include "perfetto/public/abi/atomic.h" | |
| #include "perfetto/public/abi/backend_type.h" | |
| #include "perfetto/public/abi/data_source_abi.h" | |
| #include "perfetto/public/abi/heap_buffer.h" | |
| #include "perfetto/public/abi/pb_decoder_abi.h" | |
| #include "perfetto/public/abi/producer_abi.h" | |
| #include "perfetto/public/abi/tracing_session_abi.h" | |
| #include "perfetto/public/abi/track_event_abi.h" | |
| #include "perfetto/public/compiler.h" | |
| #include "perfetto/public/data_source.h" | |
| #include "perfetto/public/pb_decoder.h" | |
| #include "perfetto/public/pb_msg.h" | |
| #include "perfetto/public/pb_packed.h" | |
| #include "perfetto/public/pb_utils.h" | |
| #include "perfetto/public/producer.h" | |
| #include "perfetto/public/protos/config/trace_config.pzc.h" | |
| #include "perfetto/public/protos/trace/interned_data/interned_data.pzc.h" | |
| #include "perfetto/public/protos/trace/test_event.pzc.h" | |
| #include "perfetto/public/protos/trace/trace.pzc.h" | |
| #include "perfetto/public/protos/trace/trace_packet.pzc.h" | |
| #include "perfetto/public/protos/trace/track_event/debug_annotation.pzc.h" | |
| #include "perfetto/public/protos/trace/track_event/track_descriptor.pzc.h" | |
| #include "perfetto/public/protos/trace/track_event/track_event.pzc.h" | |
| #include "perfetto/public/protos/trace/trigger.pzc.h" | |
| #include "perfetto/public/stream_writer.h" | |
| #include "perfetto/public/te_category_macros.h" | |
| #include "perfetto/public/te_macros.h" | |
| #include "perfetto/public/tracing_session.h" | |
| #include "perfetto/public/track_event.h" | |
| #include "test/gtest_and_gmock.h" | |
| #include "src/shared_lib/reset_for_testing.h" | |
| #include "src/shared_lib/test/protos/extensions.pzc.h" | |
| #include "src/shared_lib/test/protos/test_messages.pzc.h" | |
| #include "src/shared_lib/test/utils.h" | |
| // Tests for the perfetto shared library. | |
| namespace { | |
| using ::perfetto::shlib::test_utils::AllFieldsWithId; | |
| using ::perfetto::shlib::test_utils::DoubleField; | |
| using ::perfetto::shlib::test_utils::FieldView; | |
| using ::perfetto::shlib::test_utils::Fixed32Field; | |
| using ::perfetto::shlib::test_utils::Fixed64Field; | |
| using ::perfetto::shlib::test_utils::FloatField; | |
| using ::perfetto::shlib::test_utils::IdFieldView; | |
| using ::perfetto::shlib::test_utils::MsgField; | |
| using ::perfetto::shlib::test_utils::PbField; | |
| using ::perfetto::shlib::test_utils::StringField; | |
| using ::perfetto::shlib::test_utils::TracingSession; | |
| using ::perfetto::shlib::test_utils::VarIntField; | |
| using ::perfetto::shlib::test_utils::WaitableEvent; | |
| using ::testing::_; | |
| using ::testing::AllOf; | |
| using ::testing::DoAll; | |
| using ::testing::ElementsAre; | |
| using ::testing::InSequence; | |
| using ::testing::IsNull; | |
| using ::testing::MockFunction; | |
| using ::testing::NiceMock; | |
| using ::testing::ResultOf; | |
| using ::testing::Return; | |
| using ::testing::SaveArg; | |
| using ::testing::StrictMock; | |
| using ::testing::UnorderedElementsAre; | |
| constexpr char kDataSourceName1[] = "dev.perfetto.example_data_source"; | |
| struct PerfettoDs data_source_1 = PERFETTO_DS_INIT(); | |
| constexpr char kDataSourceName2[] = "dev.perfetto.example_data_source2"; | |
| struct PerfettoDs data_source_2 = PERFETTO_DS_INIT(); | |
| void* const kDataSource2UserArg = reinterpret_cast<void*>(0x555); | |
| constexpr char kDataSourceNameProtoVm[] = | |
| "dev.perfetto.example_data_source_protovm"; | |
| struct PerfettoDs data_source_protovm = PERFETTO_DS_INIT(); | |
| constexpr uint8_t kProtoVmProgram[] = {0x08, 0x01}; // proto = { version: 1} | |
| #define TEST_CATEGORIES(C) \ | |
| C(cat1, "cat1", "") C(cat2, "cat2", "") C(cat3, "cat3", "") | |
| PERFETTO_TE_CATEGORIES_DEFINE(TEST_CATEGORIES) | |
| class MockDs2Callbacks : testing::Mock { | |
| public: | |
| MOCK_METHOD(void*, | |
| OnSetup, | |
| (struct PerfettoDsImpl*, | |
| PerfettoDsInstanceIndex inst_id, | |
| void* ds_config, | |
| size_t ds_config_size, | |
| void* user_arg, | |
| struct PerfettoDsOnSetupArgs* args)); | |
| MOCK_METHOD(void, | |
| OnStart, | |
| (struct PerfettoDsImpl*, | |
| PerfettoDsInstanceIndex inst_id, | |
| void* user_arg, | |
| void* inst_ctx, | |
| struct PerfettoDsOnStartArgs* args)); | |
| MOCK_METHOD(void, | |
| OnStop, | |
| (struct PerfettoDsImpl*, | |
| PerfettoDsInstanceIndex inst_id, | |
| void* user_arg, | |
| void* inst_ctx, | |
| struct PerfettoDsOnStopArgs* args)); | |
| MOCK_METHOD(void, | |
| OnDestroy, | |
| (struct PerfettoDsImpl*, void* user_arg, void* inst_ctx)); | |
| MOCK_METHOD(void, | |
| OnFlush, | |
| (struct PerfettoDsImpl*, | |
| PerfettoDsInstanceIndex inst_id, | |
| void* user_arg, | |
| void* inst_ctx, | |
| struct PerfettoDsOnFlushArgs* args)); | |
| MOCK_METHOD(void*, | |
| OnCreateTls, | |
| (struct PerfettoDsImpl*, | |
| PerfettoDsInstanceIndex inst_id, | |
| struct PerfettoDsTracerImpl* tracer, | |
| void* user_arg)); | |
| MOCK_METHOD(void, OnDeleteTls, (void*)); | |
| MOCK_METHOD(void*, | |
| OnCreateIncr, | |
| (struct PerfettoDsImpl*, | |
| PerfettoDsInstanceIndex inst_id, | |
| struct PerfettoDsTracerImpl* tracer, | |
| void* user_arg)); | |
| MOCK_METHOD(void, OnDeleteIncr, (void*)); | |
| MOCK_METHOD(bool, OnClearIncr, (void*, void*)); | |
| }; | |
| TEST(SharedLibProtobufTest, PerfettoPbDecoderIteratorExample) { | |
| // # proto-message: perfetto.protos.TestEvent | |
| // counter: 5 | |
| // payload { | |
| // str: "hello" | |
| // single_int: -1 | |
| // } | |
| std::string_view msg = | |
| "\x18\x05\x2a\x12\x0a\x05\x68\x65\x6c\x6c\x6f\x28\xff\xff\xff\xff\xff\xff" | |
| "\xff\xff\xff\x01"; | |
| size_t n_counter = 0; | |
| size_t n_payload = 0; | |
| size_t n_payload_str = 0; | |
| size_t n_payload_single_int = 0; | |
| for (struct PerfettoPbDecoderIterator it = | |
| PerfettoPbDecoderIterateBegin(msg.data(), msg.size()); | |
| it.field.status != PERFETTO_PB_DECODER_DONE; | |
| PerfettoPbDecoderIterateNext(&it)) { | |
| if (it.field.status != PERFETTO_PB_DECODER_OK) { | |
| ADD_FAILURE() << "Failed to parse main message"; | |
| break; | |
| } | |
| switch (it.field.id) { | |
| case perfetto_protos_TestEvent_counter_field_number: | |
| n_counter++; | |
| EXPECT_EQ(it.field.wire_type, PERFETTO_PB_WIRE_TYPE_VARINT); | |
| { | |
| uint64_t val = 0; | |
| EXPECT_TRUE(PerfettoPbDecoderFieldGetUint64(&it.field, &val)); | |
| EXPECT_EQ(val, 5u); | |
| } | |
| break; | |
| case perfetto_protos_TestEvent_payload_field_number: | |
| n_payload++; | |
| EXPECT_EQ(it.field.wire_type, PERFETTO_PB_WIRE_TYPE_DELIMITED); | |
| for (struct PerfettoPbDecoderIterator it2 = | |
| PerfettoPbDecoderIterateNestedBegin(it.field.value.delimited); | |
| it2.field.status != PERFETTO_PB_DECODER_DONE; | |
| PerfettoPbDecoderIterateNext(&it2)) { | |
| if (it2.field.status != PERFETTO_PB_DECODER_OK) { | |
| ADD_FAILURE() << "Failed to parse nested message"; | |
| break; | |
| } | |
| switch (it2.field.id) { | |
| case perfetto_protos_TestEvent_TestPayload_str_field_number: | |
| n_payload_str++; | |
| EXPECT_EQ(it2.field.wire_type, PERFETTO_PB_WIRE_TYPE_DELIMITED); | |
| EXPECT_EQ(std::string_view(reinterpret_cast<const char*>( | |
| it2.field.value.delimited.start), | |
| it2.field.value.delimited.len), | |
| "hello"); | |
| break; | |
| case perfetto_protos_TestEvent_TestPayload_single_int_field_number: | |
| EXPECT_EQ(it2.field.wire_type, PERFETTO_PB_WIRE_TYPE_VARINT); | |
| n_payload_single_int++; | |
| { | |
| int32_t val = 0; | |
| EXPECT_TRUE(PerfettoPbDecoderFieldGetInt32(&it2.field, &val)); | |
| EXPECT_EQ(val, -1); | |
| } | |
| break; | |
| default: | |
| ADD_FAILURE() << "Unexpected nested field.id"; | |
| break; | |
| } | |
| } | |
| break; | |
| default: | |
| ADD_FAILURE() << "Unexpected field.id"; | |
| break; | |
| } | |
| } | |
| EXPECT_EQ(n_counter, 1u); | |
| EXPECT_EQ(n_payload, 1u); | |
| EXPECT_EQ(n_payload_str, 1u); | |
| EXPECT_EQ(n_payload_single_int, 1u); | |
| } | |
| class SharedLibProtozeroSerializationTest : public testing::Test { | |
| protected: | |
| SharedLibProtozeroSerializationTest() { | |
| hb = PerfettoHeapBufferCreate(&writer.writer); | |
| } | |
| std::vector<uint8_t> GetData() { | |
| std::vector<uint8_t> data; | |
| size_t size = PerfettoStreamWriterGetWrittenSize(&writer.writer); | |
| data.resize(size); | |
| PerfettoHeapBufferCopyInto(hb, &writer.writer, data.data(), data.size()); | |
| return data; | |
| } | |
| ~SharedLibProtozeroSerializationTest() { | |
| PerfettoHeapBufferDestroy(hb, &writer.writer); | |
| } | |
| template <typename T> | |
| static std::vector<T> ParsePackedVarInt(const std::string& data) { | |
| std::vector<T> ret; | |
| const uint8_t* read_ptr = reinterpret_cast<const uint8_t*>(data.data()); | |
| const uint8_t* const end = read_ptr + data.size(); | |
| while (read_ptr != end) { | |
| uint64_t val; | |
| const uint8_t* new_read_ptr = PerfettoPbParseVarInt(read_ptr, end, &val); | |
| if (new_read_ptr == read_ptr) { | |
| ADD_FAILURE(); | |
| return ret; | |
| } | |
| read_ptr = new_read_ptr; | |
| ret.push_back(static_cast<T>(val)); | |
| } | |
| return ret; | |
| } | |
| template <typename T> | |
| static std::vector<T> ParsePackedFixed(const std::string& data) { | |
| std::vector<T> ret; | |
| if (data.size() % sizeof(T)) { | |
| ADD_FAILURE(); | |
| return ret; | |
| } | |
| const uint8_t* read_ptr = reinterpret_cast<const uint8_t*>(data.data()); | |
| const uint8_t* end = read_ptr + data.size(); | |
| while (read_ptr < end) { | |
| ret.push_back(*reinterpret_cast<const T*>(read_ptr)); | |
| read_ptr += sizeof(T); | |
| } | |
| return ret; | |
| } | |
| struct PerfettoPbMsgWriter writer; | |
| struct PerfettoHeapBuffer* hb; | |
| }; | |
| TEST_F(SharedLibProtozeroSerializationTest, SimpleFieldsNoNesting) { | |
| struct protozero_test_protos_EveryField msg; | |
| PerfettoPbMsgInit(&msg.msg, &writer); | |
| protozero_test_protos_EveryField_set_field_int32(&msg, -1); | |
| protozero_test_protos_EveryField_set_field_int64(&msg, -333123456789ll); | |
| protozero_test_protos_EveryField_set_field_uint32(&msg, 600); | |
| protozero_test_protos_EveryField_set_field_uint64(&msg, 333123456789ll); | |
| protozero_test_protos_EveryField_set_field_sint32(&msg, -5); | |
| protozero_test_protos_EveryField_set_field_sint64(&msg, -9000); | |
| protozero_test_protos_EveryField_set_field_fixed32(&msg, 12345); | |
| protozero_test_protos_EveryField_set_field_fixed64(&msg, 444123450000ll); | |
| protozero_test_protos_EveryField_set_field_sfixed32(&msg, -69999); | |
| protozero_test_protos_EveryField_set_field_sfixed64(&msg, -200); | |
| protozero_test_protos_EveryField_set_field_float(&msg, 3.14f); | |
| protozero_test_protos_EveryField_set_field_double(&msg, 0.5555); | |
| protozero_test_protos_EveryField_set_field_bool(&msg, true); | |
| protozero_test_protos_EveryField_set_small_enum(&msg, | |
| protozero_test_protos_TO_BE); | |
| protozero_test_protos_EveryField_set_signed_enum( | |
| &msg, protozero_test_protos_NEGATIVE); | |
| protozero_test_protos_EveryField_set_big_enum(&msg, | |
| protozero_test_protos_BEGIN); | |
| protozero_test_protos_EveryField_set_cstr_field_string(&msg, "FizzBuzz"); | |
| protozero_test_protos_EveryField_set_field_bytes(&msg, "\x11\x00\xBE\xEF", 4); | |
| protozero_test_protos_EveryField_set_repeated_int32(&msg, 1); | |
| protozero_test_protos_EveryField_set_repeated_int32(&msg, -1); | |
| protozero_test_protos_EveryField_set_repeated_int32(&msg, 100); | |
| protozero_test_protos_EveryField_set_repeated_int32(&msg, 2000000); | |
| EXPECT_THAT( | |
| FieldView(GetData()), | |
| ElementsAre( | |
| PbField(protozero_test_protos_EveryField_field_int32_field_number, | |
| VarIntField(static_cast<uint64_t>(-1))), | |
| PbField(protozero_test_protos_EveryField_field_int64_field_number, | |
| VarIntField(static_cast<uint64_t>(INT64_C(-333123456789)))), | |
| PbField(protozero_test_protos_EveryField_field_uint32_field_number, | |
| VarIntField(600)), | |
| PbField(protozero_test_protos_EveryField_field_uint64_field_number, | |
| VarIntField(UINT64_C(333123456789))), | |
| PbField(protozero_test_protos_EveryField_field_sint32_field_number, | |
| VarIntField(ResultOf( | |
| [](uint64_t val) { | |
| return PerfettoPbZigZagDecode32( | |
| static_cast<uint32_t>(val)); | |
| }, | |
| -5))), | |
| PbField(protozero_test_protos_EveryField_field_sint64_field_number, | |
| VarIntField(ResultOf(PerfettoPbZigZagDecode64, -9000))), | |
| PbField(protozero_test_protos_EveryField_field_fixed32_field_number, | |
| Fixed32Field(12345)), | |
| PbField(protozero_test_protos_EveryField_field_fixed64_field_number, | |
| Fixed64Field(UINT64_C(444123450000))), | |
| PbField(protozero_test_protos_EveryField_field_sfixed32_field_number, | |
| Fixed32Field(static_cast<uint32_t>(-69999))), | |
| PbField(protozero_test_protos_EveryField_field_sfixed64_field_number, | |
| Fixed64Field(static_cast<uint64_t>(-200))), | |
| PbField(protozero_test_protos_EveryField_field_float_field_number, | |
| FloatField(3.14f)), | |
| PbField(protozero_test_protos_EveryField_field_double_field_number, | |
| DoubleField(0.5555)), | |
| PbField(protozero_test_protos_EveryField_field_bool_field_number, | |
| VarIntField(true)), | |
| PbField(protozero_test_protos_EveryField_small_enum_field_number, | |
| VarIntField(protozero_test_protos_TO_BE)), | |
| PbField(protozero_test_protos_EveryField_signed_enum_field_number, | |
| VarIntField(protozero_test_protos_NEGATIVE)), | |
| PbField(protozero_test_protos_EveryField_big_enum_field_number, | |
| VarIntField(protozero_test_protos_BEGIN)), | |
| PbField(protozero_test_protos_EveryField_field_string_field_number, | |
| StringField("FizzBuzz")), | |
| PbField(protozero_test_protos_EveryField_field_bytes_field_number, | |
| StringField(std::string_view("\x11\x00\xBE\xEF", 4))), | |
| PbField(protozero_test_protos_EveryField_repeated_int32_field_number, | |
| VarIntField(1)), | |
| PbField(protozero_test_protos_EveryField_repeated_int32_field_number, | |
| VarIntField(static_cast<uint64_t>(-1))), | |
| PbField(protozero_test_protos_EveryField_repeated_int32_field_number, | |
| VarIntField(100)), | |
| PbField(protozero_test_protos_EveryField_repeated_int32_field_number, | |
| VarIntField(2000000)))); | |
| } | |
| TEST_F(SharedLibProtozeroSerializationTest, NestedMessages) { | |
| struct protozero_test_protos_NestedA msg_a; | |
| PerfettoPbMsgInit(&msg_a.msg, &writer); | |
| { | |
| struct protozero_test_protos_NestedA_NestedB msg_b; | |
| protozero_test_protos_NestedA_begin_repeated_a(&msg_a, &msg_b); | |
| { | |
| struct protozero_test_protos_NestedA_NestedB_NestedC msg_c; | |
| protozero_test_protos_NestedA_NestedB_begin_value_b(&msg_b, &msg_c); | |
| protozero_test_protos_NestedA_NestedB_NestedC_set_value_c(&msg_c, 321); | |
| protozero_test_protos_NestedA_NestedB_end_value_b(&msg_b, &msg_c); | |
| } | |
| protozero_test_protos_NestedA_end_repeated_a(&msg_a, &msg_b); | |
| } | |
| { | |
| struct protozero_test_protos_NestedA_NestedB msg_b; | |
| protozero_test_protos_NestedA_begin_repeated_a(&msg_a, &msg_b); | |
| protozero_test_protos_NestedA_end_repeated_a(&msg_a, &msg_b); | |
| } | |
| { | |
| struct protozero_test_protos_NestedA_NestedB_NestedC msg_c; | |
| protozero_test_protos_NestedA_begin_super_nested(&msg_a, &msg_c); | |
| protozero_test_protos_NestedA_NestedB_NestedC_set_value_c(&msg_c, 1000); | |
| protozero_test_protos_NestedA_end_super_nested(&msg_a, &msg_c); | |
| } | |
| EXPECT_THAT( | |
| FieldView(GetData()), | |
| ElementsAre( | |
| PbField( | |
| protozero_test_protos_NestedA_repeated_a_field_number, | |
| MsgField(ElementsAre(PbField( | |
| protozero_test_protos_NestedA_NestedB_value_b_field_number, | |
| MsgField(ElementsAre(PbField( | |
| protozero_test_protos_NestedA_NestedB_NestedC_value_c_field_number, | |
| VarIntField(321)))))))), | |
| PbField(protozero_test_protos_NestedA_repeated_a_field_number, | |
| MsgField(ElementsAre())), | |
| PbField( | |
| protozero_test_protos_NestedA_super_nested_field_number, | |
| MsgField(ElementsAre(PbField( | |
| protozero_test_protos_NestedA_NestedB_NestedC_value_c_field_number, | |
| VarIntField(1000))))))); | |
| } | |
| TEST_F(SharedLibProtozeroSerializationTest, Extensions) { | |
| struct protozero_test_protos_RealFakeEvent base; | |
| PerfettoPbMsgInit(&base.msg, &writer); | |
| { | |
| struct protozero_test_protos_SystemA msg_a; | |
| protozero_test_protos_BrowserExtension_begin_extension_a(&base, &msg_a); | |
| protozero_test_protos_SystemA_set_cstr_string_a(&msg_a, "str_a"); | |
| protozero_test_protos_BrowserExtension_end_extension_a(&base, &msg_a); | |
| } | |
| { | |
| struct protozero_test_protos_SystemB msg_b; | |
| protozero_test_protos_BrowserExtension_begin_extension_b(&base, &msg_b); | |
| protozero_test_protos_SystemB_set_cstr_string_b(&msg_b, "str_b"); | |
| protozero_test_protos_BrowserExtension_end_extension_b(&base, &msg_b); | |
| } | |
| protozero_test_protos_RealFakeEvent_set_cstr_base_string(&base, "str"); | |
| EXPECT_THAT( | |
| FieldView(GetData()), | |
| ElementsAre( | |
| PbField( | |
| protozero_test_protos_BrowserExtension_extension_a_field_number, | |
| MsgField(ElementsAre( | |
| PbField(protozero_test_protos_SystemA_string_a_field_number, | |
| StringField("str_a"))))), | |
| PbField( | |
| protozero_test_protos_BrowserExtension_extension_b_field_number, | |
| MsgField(ElementsAre( | |
| PbField(protozero_test_protos_SystemB_string_b_field_number, | |
| StringField("str_b"))))), | |
| PbField(protozero_test_protos_RealFakeEvent_base_string_field_number, | |
| StringField("str")))); | |
| } | |
| TEST_F(SharedLibProtozeroSerializationTest, PackedRepeatedMsgVarInt) { | |
| struct protozero_test_protos_PackedRepeatedFields msg; | |
| PerfettoPbMsgInit(&msg.msg, &writer); | |
| { | |
| PerfettoPbPackedMsgInt32 f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_int32(&msg, &f); | |
| PerfettoPbPackedMsgInt32Append(&f, 42); | |
| PerfettoPbPackedMsgInt32Append(&f, 255); | |
| PerfettoPbPackedMsgInt32Append(&f, -1); | |
| protozero_test_protos_PackedRepeatedFields_end_field_int32(&msg, &f); | |
| } | |
| { | |
| PerfettoPbPackedMsgInt64 f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_int64(&msg, &f); | |
| PerfettoPbPackedMsgInt64Append(&f, INT64_C(3000000000)); | |
| PerfettoPbPackedMsgInt64Append(&f, INT64_C(-3000000000)); | |
| protozero_test_protos_PackedRepeatedFields_end_field_int64(&msg, &f); | |
| } | |
| { | |
| PerfettoPbPackedMsgUint32 f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_uint32(&msg, &f); | |
| PerfettoPbPackedMsgUint32Append(&f, 42); | |
| PerfettoPbPackedMsgUint32Append(&f, UINT32_C(3000000000)); | |
| protozero_test_protos_PackedRepeatedFields_end_field_uint32(&msg, &f); | |
| } | |
| { | |
| PerfettoPbPackedMsgUint64 f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_uint64(&msg, &f); | |
| PerfettoPbPackedMsgUint64Append(&f, 42); | |
| PerfettoPbPackedMsgUint64Append(&f, UINT64_C(5000000000)); | |
| protozero_test_protos_PackedRepeatedFields_end_field_uint64(&msg, &f); | |
| } | |
| { | |
| PerfettoPbPackedMsgInt32 f; | |
| protozero_test_protos_PackedRepeatedFields_begin_signed_enum(&msg, &f); | |
| PerfettoPbPackedMsgInt32Append(&f, protozero_test_protos_POSITIVE); | |
| PerfettoPbPackedMsgInt32Append(&f, protozero_test_protos_NEGATIVE); | |
| protozero_test_protos_PackedRepeatedFields_end_signed_enum(&msg, &f); | |
| } | |
| EXPECT_THAT( | |
| FieldView(GetData()), | |
| ElementsAre( | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_int32_field_number, | |
| StringField(ResultOf(ParsePackedVarInt<int32_t>, | |
| ElementsAre(42, 255, -1)))), | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_int64_field_number, | |
| StringField(ResultOf( | |
| ParsePackedVarInt<int64_t>, | |
| ElementsAre(INT64_C(3000000000), INT64_C(-3000000000))))), | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_uint32_field_number, | |
| StringField(ResultOf(ParsePackedVarInt<uint32_t>, | |
| ElementsAre(42, UINT32_C(3000000000))))), | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_uint64_field_number, | |
| StringField(ResultOf(ParsePackedVarInt<uint64_t>, | |
| ElementsAre(42, UINT64_C(5000000000))))), | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_signed_enum_field_number, | |
| StringField( | |
| ResultOf(ParsePackedVarInt<int32_t>, | |
| ElementsAre(protozero_test_protos_POSITIVE, | |
| protozero_test_protos_NEGATIVE)))))); | |
| } | |
| TEST_F(SharedLibProtozeroSerializationTest, PackedRepeatedMsgFixed) { | |
| struct protozero_test_protos_PackedRepeatedFields msg; | |
| PerfettoPbMsgInit(&msg.msg, &writer); | |
| { | |
| PerfettoPbPackedMsgFixed32 f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_fixed32(&msg, &f); | |
| PerfettoPbPackedMsgFixed32Append(&f, 42); | |
| PerfettoPbPackedMsgFixed32Append(&f, UINT32_C(3000000000)); | |
| protozero_test_protos_PackedRepeatedFields_end_field_fixed32(&msg, &f); | |
| } | |
| { | |
| PerfettoPbPackedMsgFixed64 f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_fixed64(&msg, &f); | |
| PerfettoPbPackedMsgFixed64Append(&f, 42); | |
| PerfettoPbPackedMsgFixed64Append(&f, UINT64_C(5000000000)); | |
| protozero_test_protos_PackedRepeatedFields_end_field_fixed64(&msg, &f); | |
| } | |
| { | |
| PerfettoPbPackedMsgSfixed32 f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_sfixed32(&msg, &f); | |
| PerfettoPbPackedMsgSfixed32Append(&f, 42); | |
| PerfettoPbPackedMsgSfixed32Append(&f, 255); | |
| PerfettoPbPackedMsgSfixed32Append(&f, -1); | |
| protozero_test_protos_PackedRepeatedFields_end_field_sfixed32(&msg, &f); | |
| } | |
| { | |
| PerfettoPbPackedMsgSfixed64 f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_sfixed64(&msg, &f); | |
| PerfettoPbPackedMsgSfixed64Append(&f, INT64_C(3000000000)); | |
| PerfettoPbPackedMsgSfixed64Append(&f, INT64_C(-3000000000)); | |
| protozero_test_protos_PackedRepeatedFields_end_field_sfixed64(&msg, &f); | |
| } | |
| { | |
| PerfettoPbPackedMsgFloat f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_float(&msg, &f); | |
| PerfettoPbPackedMsgFloatAppend(&f, 3.14f); | |
| PerfettoPbPackedMsgFloatAppend(&f, 42.1f); | |
| protozero_test_protos_PackedRepeatedFields_end_field_float(&msg, &f); | |
| } | |
| { | |
| PerfettoPbPackedMsgDouble f; | |
| protozero_test_protos_PackedRepeatedFields_begin_field_double(&msg, &f); | |
| PerfettoPbPackedMsgDoubleAppend(&f, 3.14); | |
| PerfettoPbPackedMsgDoubleAppend(&f, 42.1); | |
| protozero_test_protos_PackedRepeatedFields_end_field_double(&msg, &f); | |
| } | |
| EXPECT_THAT( | |
| FieldView(GetData()), | |
| ElementsAre( | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_fixed32_field_number, | |
| StringField(ResultOf(ParsePackedFixed<uint32_t>, | |
| ElementsAre(42, UINT32_C(3000000000))))), | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_fixed64_field_number, | |
| StringField(ResultOf(ParsePackedFixed<uint64_t>, | |
| ElementsAre(42, UINT64_C(5000000000))))), | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_sfixed32_field_number, | |
| StringField(ResultOf(ParsePackedFixed<int32_t>, | |
| ElementsAre(42, 255, -1)))), | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_sfixed64_field_number, | |
| StringField(ResultOf( | |
| ParsePackedFixed<int64_t>, | |
| ElementsAre(INT64_C(3000000000), INT64_C(-3000000000))))), | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_float_field_number, | |
| StringField(ResultOf(ParsePackedFixed<float>, | |
| ElementsAre(3.14f, 42.1f)))), | |
| PbField( | |
| protozero_test_protos_PackedRepeatedFields_field_double_field_number, | |
| StringField(ResultOf(ParsePackedFixed<double>, | |
| ElementsAre(3.14, 42.1)))))); | |
| } | |
| class SharedLibDataSourceTest : public testing::Test { | |
| protected: | |
| void SetUp() override { | |
| struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); | |
| args.backends = PERFETTO_BACKEND_IN_PROCESS; | |
| PerfettoProducerInit(args); | |
| PerfettoDsRegister(&data_source_1, kDataSourceName1, | |
| PerfettoDsParamsDefault()); | |
| RegisterDataSource2(); | |
| RegisterDataSourceProtoVm(); | |
| } | |
| void TearDown() override { | |
| perfetto::shlib::ResetForTesting(); | |
| data_source_1.enabled = &perfetto_atomic_false; | |
| perfetto::shlib::DsImplDestroy(data_source_1.impl); | |
| data_source_1.impl = nullptr; | |
| data_source_2.enabled = &perfetto_atomic_false; | |
| perfetto::shlib::DsImplDestroy(data_source_2.impl); | |
| data_source_2.impl = nullptr; | |
| data_source_protovm.enabled = &perfetto_atomic_false; | |
| perfetto::shlib::DsImplDestroy(data_source_protovm.impl); | |
| data_source_protovm.impl = nullptr; | |
| } | |
| struct Ds2CustomState { | |
| void* actual; | |
| SharedLibDataSourceTest* thiz; | |
| }; | |
| void RegisterDataSource2() { | |
| struct PerfettoDsParams params = PerfettoDsParamsDefault(); | |
| params.on_setup_cb = [](struct PerfettoDsImpl* ds_impl, | |
| PerfettoDsInstanceIndex inst_id, void* ds_config, | |
| size_t ds_config_size, void* user_arg, | |
| struct PerfettoDsOnSetupArgs* args) -> void* { | |
| auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg); | |
| return thiz->ds2_callbacks_.OnSetup(ds_impl, inst_id, ds_config, | |
| ds_config_size, thiz->ds2_user_arg_, | |
| args); | |
| }; | |
| params.on_start_cb = [](struct PerfettoDsImpl* ds_impl, | |
| PerfettoDsInstanceIndex inst_id, void* user_arg, | |
| void* inst_ctx, | |
| struct PerfettoDsOnStartArgs* args) -> void { | |
| auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg); | |
| return thiz->ds2_callbacks_.OnStart(ds_impl, inst_id, thiz->ds2_user_arg_, | |
| inst_ctx, args); | |
| }; | |
| params.on_stop_cb = [](struct PerfettoDsImpl* ds_impl, | |
| PerfettoDsInstanceIndex inst_id, void* user_arg, | |
| void* inst_ctx, struct PerfettoDsOnStopArgs* args) { | |
| auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg); | |
| return thiz->ds2_callbacks_.OnStop(ds_impl, inst_id, thiz->ds2_user_arg_, | |
| inst_ctx, args); | |
| }; | |
| params.on_destroy_cb = [](struct PerfettoDsImpl* ds_impl, void* user_arg, | |
| void* inst_ctx) -> void { | |
| auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg); | |
| thiz->ds2_callbacks_.OnDestroy(ds_impl, thiz->ds2_user_arg_, inst_ctx); | |
| }; | |
| params.on_flush_cb = | |
| [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id, | |
| void* user_arg, void* inst_ctx, struct PerfettoDsOnFlushArgs* args) { | |
| auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg); | |
| return thiz->ds2_callbacks_.OnFlush( | |
| ds_impl, inst_id, thiz->ds2_user_arg_, inst_ctx, args); | |
| }; | |
| params.on_create_tls_cb = | |
| [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id, | |
| struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* { | |
| auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg); | |
| auto* state = new Ds2CustomState(); | |
| state->thiz = thiz; | |
| state->actual = thiz->ds2_callbacks_.OnCreateTls(ds_impl, inst_id, tracer, | |
| thiz->ds2_user_arg_); | |
| return state; | |
| }; | |
| params.on_delete_tls_cb = [](void* ptr) { | |
| auto* state = static_cast<Ds2CustomState*>(ptr); | |
| state->thiz->ds2_callbacks_.OnDeleteTls(state->actual); | |
| delete state; | |
| }; | |
| params.on_create_incr_cb = | |
| [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id, | |
| struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* { | |
| auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg); | |
| auto* state = new Ds2CustomState(); | |
| state->thiz = thiz; | |
| state->actual = thiz->ds2_callbacks_.OnCreateIncr( | |
| ds_impl, inst_id, tracer, thiz->ds2_user_arg_); | |
| return state; | |
| }; | |
| params.on_delete_incr_cb = [](void* ptr) { | |
| auto* state = static_cast<Ds2CustomState*>(ptr); | |
| state->thiz->ds2_callbacks_.OnDeleteIncr(state->actual); | |
| delete state; | |
| }; | |
| params.on_clear_incr_cb = [](void* ptr, void* user_arg) -> bool { | |
| auto* state = static_cast<Ds2CustomState*>(ptr); | |
| return state->thiz->ds2_callbacks_.OnClearIncr(state->actual, user_arg); | |
| }; | |
| params.user_arg = this; | |
| PerfettoDsRegister(&data_source_2, kDataSourceName2, params); | |
| } | |
| void* Ds2ActualCustomState(void* ptr) { | |
| auto* state = static_cast<Ds2CustomState*>(ptr); | |
| return state->actual; | |
| } | |
| void RegisterDataSourceProtoVm() { | |
| struct PerfettoDsParams params = PerfettoDsParamsDefault(); | |
| params.protovm_program = kProtoVmProgram; | |
| params.protovm_program_size = sizeof(kProtoVmProgram); | |
| PerfettoDsRegister(&data_source_protovm, kDataSourceNameProtoVm, params); | |
| } | |
| NiceMock<MockDs2Callbacks> ds2_callbacks_; | |
| void* ds2_user_arg_ = kDataSource2UserArg; | |
| }; | |
| TEST_F(SharedLibDataSourceTest, DisabledNotExecuted) { | |
| bool executed = false; | |
| PERFETTO_DS_TRACE(data_source_1, ctx) { | |
| executed = true; | |
| } | |
| EXPECT_FALSE(executed); | |
| } | |
| TEST_F(SharedLibDataSourceTest, EnabledOnce) { | |
| size_t executed = 0; | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| PERFETTO_DS_TRACE(data_source_1, ctx) { | |
| executed++; | |
| } | |
| EXPECT_EQ(executed, 1u); | |
| } | |
| TEST_F(SharedLibDataSourceTest, EnabledTwice) { | |
| size_t executed = 0; | |
| TracingSession tracing_session1 = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| TracingSession tracing_session2 = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| PERFETTO_DS_TRACE(data_source_1, ctx) { | |
| executed++; | |
| } | |
| EXPECT_EQ(executed, 2u); | |
| } | |
| TEST_F(SharedLibDataSourceTest, Serialization) { | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| PERFETTO_DS_TRACE(data_source_1, ctx) { | |
| struct PerfettoDsRootTracePacket trace_packet; | |
| PerfettoDsTracerPacketBegin(&ctx, &trace_packet); | |
| { | |
| struct perfetto_protos_TestEvent for_testing; | |
| perfetto_protos_TracePacket_begin_for_testing(&trace_packet.msg, | |
| &for_testing); | |
| { | |
| struct perfetto_protos_TestEvent_TestPayload payload; | |
| perfetto_protos_TestEvent_begin_payload(&for_testing, &payload); | |
| perfetto_protos_TestEvent_TestPayload_set_cstr_str(&payload, | |
| "ABCDEFGH"); | |
| perfetto_protos_TestEvent_end_payload(&for_testing, &payload); | |
| } | |
| perfetto_protos_TracePacket_end_for_testing(&trace_packet.msg, | |
| &for_testing); | |
| } | |
| PerfettoDsTracerPacketEnd(&ctx, &trace_packet); | |
| } | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| bool found_for_testing = false; | |
| for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView for_testing( | |
| trace_field, perfetto_protos_TracePacket_for_testing_field_number); | |
| ASSERT_TRUE(for_testing.ok()); | |
| if (for_testing.size() == 0) { | |
| continue; | |
| } | |
| found_for_testing = true; | |
| ASSERT_EQ(for_testing.size(), 1u); | |
| ASSERT_THAT(FieldView(for_testing.front()), | |
| ElementsAre(PbField( | |
| perfetto_protos_TestEvent_payload_field_number, | |
| MsgField(ElementsAre(PbField( | |
| perfetto_protos_TestEvent_TestPayload_str_field_number, | |
| StringField("ABCDEFGH"))))))); | |
| } | |
| EXPECT_TRUE(found_for_testing); | |
| } | |
| TEST_F(SharedLibDataSourceTest, Break) { | |
| TracingSession tracing_session1 = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| TracingSession tracing_session2 = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| PERFETTO_DS_TRACE(data_source_1, ctx) { | |
| struct PerfettoDsRootTracePacket trace_packet; | |
| PerfettoDsTracerPacketBegin(&ctx, &trace_packet); | |
| { | |
| struct perfetto_protos_TestEvent for_testing; | |
| perfetto_protos_TracePacket_begin_for_testing(&trace_packet.msg, | |
| &for_testing); | |
| perfetto_protos_TracePacket_end_for_testing(&trace_packet.msg, | |
| &for_testing); | |
| } | |
| PerfettoDsTracerPacketEnd(&ctx, &trace_packet); | |
| // Break: the packet will be emitted only on the first data source instance | |
| // and therefore will not show up on `tracing_session2`. | |
| PERFETTO_DS_TRACE_BREAK(data_source_1, ctx); | |
| } | |
| tracing_session1.StopBlocking(); | |
| std::vector<uint8_t> data1 = tracing_session1.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data1), | |
| Contains(PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(Contains(PbField( | |
| perfetto_protos_TracePacket_for_testing_field_number, | |
| MsgField(_))))))); | |
| tracing_session2.StopBlocking(); | |
| std::vector<uint8_t> data2 = tracing_session2.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data2), | |
| Each(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| MsgField(Not(Contains(PbField( | |
| perfetto_protos_TracePacket_for_testing_field_number, _))))))); | |
| } | |
| TEST_F(SharedLibDataSourceTest, FlushCb) { | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| WaitableEvent notification; | |
| PERFETTO_DS_TRACE(data_source_1, ctx) { | |
| PerfettoDsTracerFlush( | |
| &ctx, | |
| [](void* p_notification) { | |
| static_cast<WaitableEvent*>(p_notification)->Notify(); | |
| }, | |
| ¬ification); | |
| } | |
| notification.WaitForNotification(); | |
| EXPECT_TRUE(notification.IsNotified()); | |
| } | |
| TEST_F(SharedLibDataSourceTest, DropCount) { | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| WaitableEvent on_flush_started; | |
| WaitableEvent on_flush_unblocked; | |
| EXPECT_CALL(ds2_callbacks_, OnFlush(_, _, _, _, _)) | |
| .WillOnce([&] { | |
| on_flush_started.Notify(); | |
| on_flush_unblocked.WaitForNotification(); | |
| }) | |
| .WillRepeatedly([] {}); | |
| // Block the internal perfetto thread inside the OnFlush callback. The | |
| // in-process tracing service runs on the same thread, so it cannot free | |
| // shared memory buffer chunks while blocked: writing enough data below is | |
| // guaranteed to exhaust the buffer and cause data loss. | |
| PerfettoTracingSessionFlushAsync(tracing_session.session(), 0, nullptr, | |
| nullptr); | |
| on_flush_started.WaitForNotification(); | |
| uint64_t initial_drop_count = 0; | |
| uint64_t final_drop_count = 0; | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| initial_drop_count = PerfettoDsTracerGetDropCount(&ctx); | |
| // Write way more data than the shared memory buffer can hold (the default | |
| // shared memory buffer size is 256 KiB). | |
| std::string large_str(1024, 'x'); | |
| for (size_t i = 0; i < 2048; i++) { | |
| struct PerfettoDsRootTracePacket trace_packet; | |
| PerfettoDsTracerPacketBegin(&ctx, &trace_packet); | |
| { | |
| struct perfetto_protos_TestEvent for_testing; | |
| perfetto_protos_TracePacket_begin_for_testing(&trace_packet.msg, | |
| &for_testing); | |
| { | |
| struct perfetto_protos_TestEvent_TestPayload payload; | |
| perfetto_protos_TestEvent_begin_payload(&for_testing, &payload); | |
| perfetto_protos_TestEvent_TestPayload_set_cstr_str(&payload, | |
| large_str.c_str()); | |
| perfetto_protos_TestEvent_end_payload(&for_testing, &payload); | |
| } | |
| perfetto_protos_TracePacket_end_for_testing(&trace_packet.msg, | |
| &for_testing); | |
| } | |
| PerfettoDsTracerPacketEnd(&ctx, &trace_packet); | |
| } | |
| final_drop_count = PerfettoDsTracerGetDropCount(&ctx); | |
| } | |
| on_flush_unblocked.Notify(); | |
| tracing_session.StopBlocking(); | |
| EXPECT_EQ(initial_drop_count, 0u); | |
| EXPECT_GT(final_drop_count, 0u); | |
| } | |
| TEST_F(SharedLibDataSourceTest, LifetimeCallbacks) { | |
| void* const kInstancePtr = reinterpret_cast<void*>(0x44); | |
| testing::InSequence seq; | |
| PerfettoDsInstanceIndex setup_inst, start_inst, stop_inst; | |
| EXPECT_CALL(ds2_callbacks_, OnSetup(_, _, _, _, kDataSource2UserArg, _)) | |
| .WillOnce(DoAll(SaveArg<1>(&setup_inst), Return(kInstancePtr))); | |
| EXPECT_CALL(ds2_callbacks_, | |
| OnStart(_, _, kDataSource2UserArg, kInstancePtr, _)) | |
| .WillOnce(SaveArg<1>(&start_inst)); | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| EXPECT_CALL(ds2_callbacks_, | |
| OnStop(_, _, kDataSource2UserArg, kInstancePtr, _)) | |
| .WillOnce(SaveArg<1>(&stop_inst)); | |
| EXPECT_CALL(ds2_callbacks_, OnDestroy(_, kDataSource2UserArg, kInstancePtr)); | |
| tracing_session.StopBlocking(); | |
| EXPECT_EQ(setup_inst, start_inst); | |
| EXPECT_EQ(setup_inst, stop_inst); | |
| } | |
| TEST_F(SharedLibDataSourceTest, StopDone) { | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| WaitableEvent stop_called; | |
| struct PerfettoDsAsyncStopper* stopper; | |
| EXPECT_CALL(ds2_callbacks_, OnStop(_, _, kDataSource2UserArg, _, _)) | |
| .WillOnce([&](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, | |
| void*, struct PerfettoDsOnStopArgs* args) { | |
| stopper = PerfettoDsOnStopArgsPostpone(args); | |
| stop_called.Notify(); | |
| }); | |
| std::thread t([&]() { tracing_session.StopBlocking(); }); | |
| stop_called.WaitForNotification(); | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| struct PerfettoDsRootTracePacket trace_packet; | |
| PerfettoDsTracerPacketBegin(&ctx, &trace_packet); | |
| { | |
| struct perfetto_protos_TestEvent for_testing; | |
| perfetto_protos_TracePacket_begin_for_testing(&trace_packet.msg, | |
| &for_testing); | |
| { | |
| struct perfetto_protos_TestEvent_TestPayload payload; | |
| perfetto_protos_TestEvent_begin_payload(&for_testing, &payload); | |
| perfetto_protos_TestEvent_TestPayload_set_cstr_str(&payload, | |
| "After stop"); | |
| perfetto_protos_TestEvent_end_payload(&for_testing, &payload); | |
| } | |
| perfetto_protos_TracePacket_end_for_testing(&trace_packet.msg, | |
| &for_testing); | |
| } | |
| PerfettoDsTracerPacketEnd(&ctx, &trace_packet); | |
| } | |
| PerfettoDsStopDone(stopper); | |
| t.join(); | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| // After the postponed stop has been acknowledged, this should not be | |
| // executed. | |
| ADD_FAILURE(); | |
| } | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| MsgField(Contains(PbField( | |
| perfetto_protos_TracePacket_for_testing_field_number, | |
| MsgField(Contains(PbField( | |
| perfetto_protos_TestEvent_payload_field_number, | |
| MsgField(ElementsAre(PbField( | |
| perfetto_protos_TestEvent_TestPayload_str_field_number, | |
| StringField("After stop"))))))))))))); | |
| } | |
| TEST_F(SharedLibDataSourceTest, FlushDone) { | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| WaitableEvent flush_called; | |
| WaitableEvent flush_done; | |
| struct PerfettoDsAsyncFlusher* flusher; | |
| EXPECT_CALL(ds2_callbacks_, OnFlush(_, _, kDataSource2UserArg, _, _)) | |
| .WillOnce([&](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, | |
| void*, struct PerfettoDsOnFlushArgs* args) { | |
| flusher = PerfettoDsOnFlushArgsPostpone(args); | |
| flush_called.Notify(); | |
| }); | |
| std::thread t([&]() { | |
| tracing_session.FlushBlocking(/*timeout_ms=*/10000); | |
| flush_done.Notify(); | |
| }); | |
| flush_called.WaitForNotification(); | |
| EXPECT_FALSE(flush_done.IsNotified()); | |
| PerfettoDsFlushDone(flusher); | |
| flush_done.WaitForNotification(); | |
| t.join(); | |
| } | |
| TEST_F(SharedLibDataSourceTest, FlushReason) { | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| uint64_t reason = PERFETTO_DS_FLUSH_REASON_UNKNOWN; | |
| WaitableEvent flush_called; | |
| EXPECT_CALL(ds2_callbacks_, OnFlush(_, _, kDataSource2UserArg, _, _)) | |
| .WillOnce([&](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, | |
| void*, struct PerfettoDsOnFlushArgs* args) { | |
| reason = PerfettoDsOnFlushArgsGetReason(args); | |
| flush_called.Notify(); | |
| }); | |
| tracing_session.FlushBlocking(/*timeout_ms=*/10000); | |
| flush_called.WaitForNotification(); | |
| EXPECT_EQ(reason, static_cast<uint64_t>(PERFETTO_DS_FLUSH_REASON_EXPLICIT)); | |
| } | |
| TEST_F(SharedLibDataSourceTest, ThreadLocalState) { | |
| bool ignored = false; | |
| void* const kTlsPtr = &ignored; | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| EXPECT_CALL(ds2_callbacks_, OnCreateTls).WillOnce(Return(kTlsPtr)); | |
| void* tls_state = nullptr; | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| tls_state = PerfettoDsGetCustomTls(&data_source_2, &ctx); | |
| } | |
| EXPECT_EQ(Ds2ActualCustomState(tls_state), kTlsPtr); | |
| tracing_session.StopBlocking(); | |
| EXPECT_CALL(ds2_callbacks_, OnDeleteTls(kTlsPtr)); | |
| // The OnDelete callback will be called by | |
| // DestroyStoppedTraceWritersForCurrentThread(). One way to trigger that is to | |
| // trace with another data source. | |
| TracingSession tracing_session_1 = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| PERFETTO_DS_TRACE(data_source_1, ctx) {} | |
| } | |
| TEST_F(SharedLibDataSourceTest, IncrementalState) { | |
| bool ignored = false; | |
| void* const kIncrPtr = &ignored; | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| EXPECT_CALL(ds2_callbacks_, OnCreateIncr).WillOnce(Return(kIncrPtr)); | |
| void* tls_state = nullptr; | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| tls_state = PerfettoDsGetIncrementalState(&data_source_2, &ctx); | |
| } | |
| EXPECT_EQ(Ds2ActualCustomState(tls_state), kIncrPtr); | |
| tracing_session.StopBlocking(); | |
| EXPECT_CALL(ds2_callbacks_, OnDeleteIncr(kIncrPtr)); | |
| // The OnDelete callback will be called by | |
| // DestroyStoppedTraceWritersForCurrentThread(). One way to trigger that is to | |
| // trace with another data source. | |
| TracingSession tracing_session_1 = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| PERFETTO_DS_TRACE(data_source_1, ctx) {} | |
| } | |
| TEST_F(SharedLibDataSourceTest, IncrementalStateClearSuccess) { | |
| bool ignored = false; | |
| void* const kIncrPtr = &ignored; | |
| WaitableEvent clear_notification; | |
| // Create tracing session with periodic incremental state clearing | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name(kDataSourceName2) | |
| .set_clear_period_ms(10) | |
| .Build(); | |
| EXPECT_CALL(ds2_callbacks_, OnCreateIncr).WillOnce(Return(kIncrPtr)); | |
| // Get incremental state - this should create it | |
| void* tls_state = nullptr; | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| tls_state = PerfettoDsGetIncrementalState(&data_source_2, &ctx); | |
| } | |
| EXPECT_EQ(Ds2ActualCustomState(tls_state), kIncrPtr); | |
| // Set up expectation that clear will be called and will return true. | |
| // It may be called multiple times since clear_period_ms keeps firing. | |
| EXPECT_CALL(ds2_callbacks_, OnClearIncr(kIncrPtr, _)) | |
| .WillRepeatedly([&clear_notification](void*, void*) { | |
| clear_notification.Notify(); | |
| return true; | |
| }); | |
| // OnDeleteIncr should NOT be called because clear succeeded | |
| EXPECT_CALL(ds2_callbacks_, OnDeleteIncr).Times(0); | |
| // Wait for at least one clear period to elapse, then access the incremental | |
| // state which will trigger the clear callback. | |
| perfetto::base::SleepMicroseconds(15 * 1000); // 15ms = 1.5x clear_period_ms | |
| // Access the incremental state multiple times to ensure clearing is triggered | |
| constexpr size_t kMaxLoops = 10; | |
| for (size_t i = 0; i < kMaxLoops && !clear_notification.IsNotified(); i++) { | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| PerfettoDsGetIncrementalState(&data_source_2, &ctx); | |
| } | |
| perfetto::base::SleepMicroseconds(10 * 1000); | |
| } | |
| ASSERT_TRUE(clear_notification.IsNotified()) | |
| << "OnClearIncr was never called"; | |
| // The incremental state should be cleared (not recreated) so we should get | |
| // the same pointer back | |
| void* tls_state_after_clear = nullptr; | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| tls_state_after_clear = PerfettoDsGetIncrementalState(&data_source_2, &ctx); | |
| } | |
| EXPECT_EQ(Ds2ActualCustomState(tls_state_after_clear), kIncrPtr); | |
| tracing_session.StopBlocking(); | |
| // Cleanup: OnDeleteIncr should be called when we trace with another data | |
| // source | |
| EXPECT_CALL(ds2_callbacks_, OnDeleteIncr(kIncrPtr)); | |
| TracingSession tracing_session_1 = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| PERFETTO_DS_TRACE(data_source_1, ctx) {} | |
| } | |
| TEST_F(SharedLibDataSourceTest, IncrementalStateClearFailure) { | |
| bool ignored1 = false; | |
| bool ignored2 = false; | |
| void* const kIncrPtr1 = &ignored1; | |
| void* const kIncrPtr2 = &ignored2; | |
| WaitableEvent clear_notification; | |
| // Create tracing session with periodic incremental state clearing | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name(kDataSourceName2) | |
| .set_clear_period_ms(10) | |
| .Build(); | |
| EXPECT_CALL(ds2_callbacks_, OnCreateIncr).WillOnce(Return(kIncrPtr1)); | |
| // Get incremental state - this should create it | |
| void* tls_state = nullptr; | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| tls_state = PerfettoDsGetIncrementalState(&data_source_2, &ctx); | |
| } | |
| EXPECT_EQ(Ds2ActualCustomState(tls_state), kIncrPtr1); | |
| // Set up expectation that clear will be called but will return false. | |
| // After the first call returns false, subsequent calls should recreate with | |
| // a new pointer. We use WillOnce to return false once, then WillRepeatedly | |
| // for subsequent attempts which should get the new pointer. | |
| EXPECT_CALL(ds2_callbacks_, OnClearIncr(kIncrPtr1, _)) | |
| .WillOnce([&clear_notification](void*, void*) { | |
| clear_notification.Notify(); | |
| return false; // Clear failed | |
| }); | |
| // OnDeleteIncr SHOULD be called because clear returned false | |
| EXPECT_CALL(ds2_callbacks_, OnDeleteIncr(kIncrPtr1)); | |
| // OnCreateIncr should be called again to recreate the state. It may be | |
| // called multiple times if clear keeps firing. | |
| EXPECT_CALL(ds2_callbacks_, OnCreateIncr).WillRepeatedly(Return(kIncrPtr2)); | |
| // OnClearIncr may be called again with the new pointer | |
| EXPECT_CALL(ds2_callbacks_, OnClearIncr(kIncrPtr2, _)) | |
| .WillRepeatedly(Return(true)); | |
| // Wait for at least one clear period to elapse, then access the incremental | |
| // state which will trigger the clear callback. | |
| perfetto::base::SleepMicroseconds(15 * 1000); // 15ms = 1.5x clear_period_ms | |
| // Access the incremental state multiple times to ensure clearing is triggered | |
| constexpr size_t kMaxLoops = 10; | |
| for (size_t i = 0; i < kMaxLoops && !clear_notification.IsNotified(); i++) { | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| PerfettoDsGetIncrementalState(&data_source_2, &ctx); | |
| } | |
| perfetto::base::SleepMicroseconds(10 * 1000); | |
| } | |
| ASSERT_TRUE(clear_notification.IsNotified()) | |
| << "OnClearIncr was never called"; | |
| // The incremental state should be recreated, so we should get a new pointer | |
| void* tls_state_after_clear = nullptr; | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| tls_state_after_clear = PerfettoDsGetIncrementalState(&data_source_2, &ctx); | |
| } | |
| EXPECT_EQ(Ds2ActualCustomState(tls_state_after_clear), kIncrPtr2); | |
| EXPECT_NE(kIncrPtr1, kIncrPtr2); | |
| tracing_session.StopBlocking(); | |
| // Cleanup: OnDeleteIncr should be called for the recreated state | |
| EXPECT_CALL(ds2_callbacks_, OnDeleteIncr(kIncrPtr2)); | |
| TracingSession tracing_session_1 = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName1).Build(); | |
| PERFETTO_DS_TRACE(data_source_1, ctx) {} | |
| } | |
| TEST_F(SharedLibDataSourceTest, GetInstanceLockedSuccess) { | |
| bool ignored = false; | |
| void* const kInstancePtr = &ignored; | |
| EXPECT_CALL(ds2_callbacks_, OnSetup(_, _, _, _, kDataSource2UserArg, _)) | |
| .WillOnce(Return(kInstancePtr)); | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| void* arg = nullptr; | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| arg = PerfettoDsImplGetInstanceLocked(data_source_2.impl, ctx.impl.inst_id); | |
| if (arg) { | |
| PerfettoDsImplReleaseInstanceLocked(data_source_2.impl, ctx.impl.inst_id); | |
| } | |
| } | |
| EXPECT_EQ(arg, kInstancePtr); | |
| } | |
| TEST_F(SharedLibDataSourceTest, GetInstanceLockedFailure) { | |
| bool ignored = false; | |
| void* const kInstancePtr = &ignored; | |
| EXPECT_CALL(ds2_callbacks_, OnSetup(_, _, _, _, kDataSource2UserArg, _)) | |
| .WillOnce(Return(kInstancePtr)); | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| WaitableEvent inside_tracing; | |
| WaitableEvent stopped; | |
| std::thread t([&] { | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| inside_tracing.Notify(); | |
| stopped.WaitForNotification(); | |
| void* arg = | |
| PerfettoDsImplGetInstanceLocked(data_source_2.impl, ctx.impl.inst_id); | |
| if (arg) { | |
| PerfettoDsImplReleaseInstanceLocked(data_source_2.impl, | |
| ctx.impl.inst_id); | |
| } | |
| EXPECT_THAT(arg, IsNull()); | |
| } | |
| }); | |
| inside_tracing.WaitForNotification(); | |
| tracing_session.StopBlocking(); | |
| stopped.Notify(); | |
| t.join(); | |
| } | |
| // Regression test for a `PerfettoDsImplReleaseInstanceLocked()`. Under very | |
| // specific circumstances, that depends on the implementation details of | |
| // `TracingMuxerImpl`, the following events can happen: | |
| // * `PerfettoDsImplGetInstanceLocked()` is called after | |
| // `TracingMuxerImpl::StopDataSource_AsyncBeginImpl`, but before | |
| // `TracingMuxerImpl::StopDataSource_AsyncEnd`. | |
| // `PerfettoDsImplGetInstanceLocked()` succeeds and returns a valid instance. | |
| // * `TracingMuxerImpl::StopDataSource_AsyncEnd()` is called. | |
| // `DataSourceStaticState::valid_instances` is reset. | |
| // * `PerfettoDsImplReleaseInstanceLocked()` is called. | |
| // | |
| // In this case `PerfettoDsImplReleaseInstanceLocked()` should work even though | |
| // the instance is not there in the valid_instances bitmap anymore. | |
| // | |
| // In order to reproduce the specific failure, the test makes assumptions about | |
| // the internal implementation (that valid_instance is changed outside of the | |
| // lock). If that were to change and the test would fail, the test should be | |
| // changed/deleted. | |
| TEST_F(SharedLibDataSourceTest, GetInstanceLockedStopBeforeRelease) { | |
| bool ignored = false; | |
| void* const kInstancePtr = &ignored; | |
| EXPECT_CALL(ds2_callbacks_, OnSetup(_, _, _, _, kDataSource2UserArg, _)) | |
| .WillOnce(Return(kInstancePtr)); | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name(kDataSourceName2).Build(); | |
| WaitableEvent inside_tracing; | |
| WaitableEvent stopping; | |
| WaitableEvent locked; | |
| WaitableEvent fully_stopped; | |
| std::thread t([&] { | |
| PERFETTO_DS_TRACE(data_source_2, ctx) { | |
| inside_tracing.Notify(); | |
| stopping.WaitForNotification(); | |
| void* arg = | |
| PerfettoDsImplGetInstanceLocked(data_source_2.impl, ctx.impl.inst_id); | |
| EXPECT_EQ(arg, kInstancePtr); | |
| locked.Notify(); | |
| fully_stopped.WaitForNotification(); | |
| if (arg) { | |
| PerfettoDsImplReleaseInstanceLocked(data_source_2.impl, | |
| ctx.impl.inst_id); | |
| } | |
| } | |
| }); | |
| inside_tracing.WaitForNotification(); | |
| struct PerfettoDsAsyncStopper* stopper = nullptr; | |
| EXPECT_CALL(ds2_callbacks_, OnStop(_, _, kDataSource2UserArg, _, _)) | |
| .WillOnce([&](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, | |
| void*, struct PerfettoDsOnStopArgs* args) { | |
| stopper = PerfettoDsOnStopArgsPostpone(args); | |
| stopping.Notify(); | |
| }); | |
| tracing_session.StopAsync(); | |
| locked.WaitForNotification(); | |
| PerfettoDsStopDone(stopper); | |
| // Wait for PerfettoDsImplTraceIterateBegin to return a nullptr tracer. This | |
| // means that the valid_instances bitmap has been reset. | |
| for (;;) { | |
| PerfettoDsImplTracerIterator iterator = | |
| PerfettoDsImplTraceIterateBegin(data_source_2.impl); | |
| if (iterator.tracer == nullptr) { | |
| break; | |
| } | |
| PerfettoDsImplTraceIterateBreak(data_source_2.impl, &iterator); | |
| std::this_thread::yield(); | |
| } | |
| fully_stopped.Notify(); | |
| tracing_session.WaitForStopped(); | |
| t.join(); | |
| } | |
| TEST_F(SharedLibDataSourceTest, GetTimestamp) { | |
| struct PerfettoDsTimestamp ts = PerfettoDsGetTimestamp(); | |
| EXPECT_TRUE(ts.clock_id == PERFETTO_DS_CLOCK_MONOTONIC || | |
| ts.clock_id == PERFETTO_DS_CLOCK_BOOTTIME); | |
| EXPECT_GT(ts.value, 0u); | |
| struct PerfettoTeTimestamp te_ts = PerfettoTeGetTimestamp(); | |
| EXPECT_EQ(te_ts.clock_id, ts.clock_id); | |
| // Values should be very close (within 1ms) as they are taken in quick | |
| // succession from the same underlying clock. | |
| EXPECT_NEAR(static_cast<double>(te_ts.value), static_cast<double>(ts.value), | |
| 1e6); | |
| } | |
| TEST_F(SharedLibDataSourceTest, ProtoVm) { | |
| TracingSession tracing_session = | |
| TracingSession::Builder() | |
| .set_data_source_name(kDataSourceNameProtoVm) | |
| .enable_protovm_config() | |
| .Build(); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| bool found_protovms = false; | |
| for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { | |
| if (trace_field.id == perfetto_protos_Trace_packet_field_number) { | |
| IdFieldView protovms_view( | |
| trace_field, perfetto_protos_TracePacket_protovms_field_number); | |
| for (struct PerfettoPbDecoderField protovms_field : protovms_view) { | |
| IdFieldView instance_view( | |
| protovms_field, | |
| perfetto_protos_TracePacket_ProtoVms_instance_field_number); | |
| for (struct PerfettoPbDecoderField instance_field : instance_view) { | |
| IdFieldView program_view( | |
| instance_field, | |
| perfetto_protos_TracePacket_ProtoVms_Instance_program_field_number); | |
| for (struct PerfettoPbDecoderField program_field : program_view) { | |
| std::string actual_bytes(reinterpret_cast<const char*>( | |
| program_field.value.delimited.start), | |
| program_field.value.delimited.len); | |
| std::string expected_bytes( | |
| reinterpret_cast<const char*>(kProtoVmProgram), | |
| sizeof(kProtoVmProgram)); | |
| EXPECT_EQ(actual_bytes, expected_bytes); | |
| found_protovms = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| EXPECT_TRUE(found_protovms); | |
| } | |
| class SharedLibProducerTest : public testing::Test { | |
| protected: | |
| void SetUp() override { | |
| struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); | |
| args.backends = PERFETTO_BACKEND_IN_PROCESS; | |
| PerfettoProducerInit(args); | |
| } | |
| void TearDown() override { perfetto::shlib::ResetForTesting(); } | |
| }; | |
| TEST_F(SharedLibProducerTest, ActivateTriggers) { | |
| struct PerfettoPbMsgWriter writer; | |
| struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer); | |
| struct perfetto_protos_TraceConfig cfg; | |
| PerfettoPbMsgInit(&cfg.msg, &writer); | |
| { | |
| struct perfetto_protos_TraceConfig_BufferConfig buffers; | |
| perfetto_protos_TraceConfig_begin_buffers(&cfg, &buffers); | |
| perfetto_protos_TraceConfig_BufferConfig_set_size_kb(&buffers, 1024); | |
| perfetto_protos_TraceConfig_end_buffers(&cfg, &buffers); | |
| } | |
| { | |
| struct perfetto_protos_TraceConfig_TriggerConfig trigger_config; | |
| perfetto_protos_TraceConfig_begin_trigger_config(&cfg, &trigger_config); | |
| perfetto_protos_TraceConfig_TriggerConfig_set_trigger_mode( | |
| &trigger_config, | |
| perfetto_protos_TraceConfig_TriggerConfig_STOP_TRACING); | |
| perfetto_protos_TraceConfig_TriggerConfig_set_trigger_timeout_ms( | |
| &trigger_config, 5000); | |
| { | |
| struct perfetto_protos_TraceConfig_TriggerConfig_Trigger trigger; | |
| perfetto_protos_TraceConfig_TriggerConfig_begin_triggers(&trigger_config, | |
| &trigger); | |
| perfetto_protos_TraceConfig_TriggerConfig_Trigger_set_cstr_name( | |
| &trigger, "trigger1"); | |
| perfetto_protos_TraceConfig_TriggerConfig_end_triggers(&trigger_config, | |
| &trigger); | |
| } | |
| perfetto_protos_TraceConfig_end_trigger_config(&cfg, &trigger_config); | |
| } | |
| size_t cfg_size = PerfettoStreamWriterGetWrittenSize(&writer.writer); | |
| std::unique_ptr<uint8_t[]> ser(new uint8_t[cfg_size]); | |
| PerfettoHeapBufferCopyInto(hb, &writer.writer, ser.get(), cfg_size); | |
| PerfettoHeapBufferDestroy(hb, &writer.writer); | |
| struct PerfettoTracingSessionImpl* ts = | |
| PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS); | |
| PerfettoTracingSessionSetup(ts, ser.get(), cfg_size); | |
| PerfettoTracingSessionStartBlocking(ts); | |
| TracingSession tracing_session = TracingSession::Adopt(ts); | |
| const char* triggers[3]; | |
| triggers[0] = "trigger0"; | |
| triggers[1] = "trigger1"; | |
| triggers[2] = nullptr; | |
| PerfettoProducerActivateTriggers(triggers, 10000); | |
| tracing_session.WaitForStopped(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| EXPECT_THAT(FieldView(data), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| MsgField(Contains(PbField( | |
| perfetto_protos_TracePacket_trigger_field_number, | |
| MsgField(Contains(PbField( | |
| perfetto_protos_Trigger_trigger_name_field_number, | |
| StringField("trigger1")))))))))); | |
| } | |
| TEST(SharedLibNonInitializedTest, DataSourceTrace) { | |
| EXPECT_FALSE(PERFETTO_ATOMIC_LOAD(data_source_1.enabled)); | |
| bool executed = false; | |
| PERFETTO_DS_TRACE(data_source_1, ctx) { | |
| executed = true; | |
| } | |
| EXPECT_FALSE(executed); | |
| } | |
| TEST(SharedLibNonInitializedTest, TeMacro) { | |
| EXPECT_FALSE(std::atomic_load(cat1.enabled)); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("")); | |
| } | |
| class SharedLibTrackEventTest : public testing::Test { | |
| protected: | |
| void SetUp() override { | |
| struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); | |
| args.backends = PERFETTO_BACKEND_IN_PROCESS; | |
| PerfettoProducerInit(args); | |
| PerfettoTeInit(); | |
| PERFETTO_TE_REGISTER_CATEGORIES(TEST_CATEGORIES); | |
| } | |
| void TearDown() override { | |
| PERFETTO_TE_UNREGISTER_CATEGORIES(TEST_CATEGORIES); | |
| perfetto::shlib::ResetForTesting(); | |
| } | |
| }; | |
| TEST_F(SharedLibTrackEventTest, TrackEventFastpathOtherDsCatDisabled) { | |
| TracingSession tracing_session = | |
| TracingSession::Builder() | |
| .set_data_source_name("other_nonexisting_datasource") | |
| .Build(); | |
| EXPECT_FALSE(std::atomic_load(cat1.enabled)); | |
| EXPECT_FALSE(std::atomic_load(cat2.enabled)); | |
| EXPECT_FALSE(std::atomic_load(cat3.enabled)); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventFastpathEmptyConfigDisablesAllCats) { | |
| ASSERT_FALSE(std::atomic_load(cat1.enabled)); | |
| ASSERT_FALSE(std::atomic_load(cat2.enabled)); | |
| ASSERT_FALSE(std::atomic_load(cat3.enabled)); | |
| TracingSession tracing_session = | |
| TracingSession::Builder().set_data_source_name("track_event").Build(); | |
| EXPECT_FALSE(std::atomic_load(cat1.enabled)); | |
| EXPECT_FALSE(std::atomic_load(cat2.enabled)); | |
| EXPECT_FALSE(std::atomic_load(cat3.enabled)); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventFastpathOneCatEnabled) { | |
| ASSERT_FALSE(std::atomic_load(cat1.enabled)); | |
| ASSERT_FALSE(std::atomic_load(cat2.enabled)); | |
| ASSERT_FALSE(std::atomic_load(cat3.enabled)); | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("cat1") | |
| .add_disabled_category("*") | |
| .Build(); | |
| EXPECT_TRUE(std::atomic_load(cat1.enabled)); | |
| EXPECT_FALSE(std::atomic_load(cat2.enabled)); | |
| EXPECT_FALSE(std::atomic_load(cat3.enabled)); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventCategoryCallback) { | |
| StrictMock<MockFunction<void(struct PerfettoTeCategoryImpl*, | |
| PerfettoDsInstanceIndex, bool, bool)>> | |
| mf; | |
| auto f = [](struct PerfettoTeCategoryImpl* cat, PerfettoDsInstanceIndex i, | |
| bool created, bool global_state_changed, void* mf) { | |
| static_cast<MockFunction<void(struct PerfettoTeCategoryImpl*, | |
| PerfettoDsInstanceIndex, bool, bool)>*>(mf) | |
| ->Call(cat, i, created, global_state_changed); | |
| }; | |
| PerfettoTeCategorySetCallback(&cat1, f, &mf); | |
| PerfettoTeCategorySetCallback(&cat2, f, &mf); | |
| PerfettoTeCategorySetCallback(&cat3, f, &mf); | |
| EXPECT_CALL( | |
| mf, Call(cat1.impl, _, /*created=*/true, /*global_state_changed=*/true)) | |
| .WillOnce(Return()); | |
| EXPECT_CALL( | |
| mf, Call(cat2.impl, _, /*created=*/true, /*global_state_changed=*/true)) | |
| .WillOnce(Return()); | |
| EXPECT_CALL(mf, Call(cat3.impl, _, /*created=*/_, /*global_state_changed=*/_)) | |
| .Times(0); | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("cat1") | |
| .add_enabled_category("cat2") | |
| .add_disabled_category("*") | |
| .Build(); | |
| testing::Mock::VerifyAndClear(&mf); | |
| EXPECT_CALL( | |
| mf, Call(cat1.impl, _, /*created=*/true, /*global_state_changed=*/false)) | |
| .WillOnce(Return()); | |
| EXPECT_CALL(mf, Call(cat2.impl, _, /*created=*/_, /*global_state_changed=*/_)) | |
| .Times(0); | |
| EXPECT_CALL(mf, Call(cat3.impl, _, /*created=*/_, /*global_state_changed=*/_)) | |
| .Times(0); | |
| TracingSession tracing_session2 = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("cat1") | |
| .add_disabled_category("*") | |
| .Build(); | |
| testing::Mock::VerifyAndClear(&mf); | |
| EXPECT_CALL( | |
| mf, Call(cat1.impl, _, /*created=*/false, /*global_state_changed=*/false)) | |
| .WillOnce(Return()); | |
| EXPECT_CALL(mf, Call(cat2.impl, _, /*created=*/_, /*global_state_changed=*/_)) | |
| .Times(0); | |
| EXPECT_CALL(mf, Call(cat3.impl, _, /*created=*/_, /*global_state_changed=*/_)) | |
| .Times(0); | |
| tracing_session2.StopBlocking(); | |
| testing::Mock::VerifyAndClear(&mf); | |
| EXPECT_CALL( | |
| mf, Call(cat1.impl, _, /*created=*/false, /*global_state_changed=*/true)) | |
| .WillOnce(Return()); | |
| EXPECT_CALL( | |
| mf, Call(cat2.impl, _, /*created=*/false, /*global_state_changed=*/true)) | |
| .WillOnce(Return()); | |
| EXPECT_CALL(mf, Call(cat3.impl, _, /*created=*/_, /*global_state_changed=*/_)) | |
| .Times(0); | |
| tracing_session.StopBlocking(); | |
| testing::Mock::VerifyAndClear(&mf); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlCategory) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| EXPECT_TRUE(std::atomic_load(cat1.enabled)); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("")); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| bool found = false; | |
| for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| found = true; | |
| IdFieldView cat_iid_fields( | |
| track_event.front(), | |
| perfetto_protos_TrackEvent_category_iids_field_number); | |
| ASSERT_THAT(cat_iid_fields, ElementsAre(VarIntField(_))); | |
| uint64_t cat_iid = cat_iid_fields.front().value.integer64; | |
| EXPECT_THAT( | |
| trace_field, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_InternedData_event_categories_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_EventCategory_iid_field_number, | |
| VarIntField(cat_iid)), | |
| PbField(perfetto_protos_EventCategory_name_field_number, | |
| StringField("cat1"))))))))); | |
| } | |
| EXPECT_TRUE(found); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlDynamicCategory) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("dyn1") | |
| .add_enabled_category("cat1") | |
| .add_disabled_category("*") | |
| .Build(); | |
| PERFETTO_TE(PERFETTO_TE_DYNAMIC_CATEGORY, PERFETTO_TE_INSTANT(""), | |
| PERFETTO_TE_DYNAMIC_CATEGORY_STRING("dyn2")); | |
| PERFETTO_TE(PERFETTO_TE_DYNAMIC_CATEGORY, PERFETTO_TE_INSTANT(""), | |
| PERFETTO_TE_DYNAMIC_CATEGORY_STRING("dyn1")); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| bool found = false; | |
| for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| found = true; | |
| EXPECT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_categories_field_number, | |
| ElementsAre(StringField("dyn1"))))); | |
| } | |
| EXPECT_TRUE(found); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlDynamicCategoryMultipleSessions) { | |
| TracingSession tracing_session1 = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("cat1") | |
| .add_enabled_category("dyn1") | |
| .add_disabled_category("dyn2") | |
| .add_disabled_category("*") | |
| .Build(); | |
| TracingSession tracing_session2 = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("cat1") | |
| .add_enabled_category("dyn2") | |
| .add_disabled_category("dyn1") | |
| .add_disabled_category("*") | |
| .Build(); | |
| PERFETTO_TE(PERFETTO_TE_DYNAMIC_CATEGORY, | |
| PERFETTO_TE_INSTANT("interned_string"), | |
| PERFETTO_TE_DYNAMIC_CATEGORY_STRING("dyn1")); | |
| PERFETTO_TE(PERFETTO_TE_DYNAMIC_CATEGORY, | |
| PERFETTO_TE_INSTANT("interned_string"), | |
| PERFETTO_TE_DYNAMIC_CATEGORY_STRING("dyn2")); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("")); | |
| tracing_session1.StopBlocking(); | |
| std::vector<uint8_t> data1 = tracing_session1.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data1), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| MsgField(AllOf( | |
| Contains(PbField( | |
| perfetto_protos_TracePacket_track_event_field_number, | |
| MsgField(Contains(PbField( | |
| perfetto_protos_TrackEvent_categories_field_number, | |
| StringField("dyn1")))))), | |
| Contains(PbField( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| MsgField(Contains(PbField( | |
| perfetto_protos_InternedData_event_names_field_number, | |
| MsgField(Contains( | |
| PbField(perfetto_protos_EventName_name_field_number, | |
| StringField("interned_string")))))))))))))); | |
| tracing_session2.StopBlocking(); | |
| std::vector<uint8_t> data2 = tracing_session2.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data2), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| MsgField(AllOf( | |
| Contains(PbField( | |
| perfetto_protos_TracePacket_track_event_field_number, | |
| MsgField(Contains(PbField( | |
| perfetto_protos_TrackEvent_categories_field_number, | |
| StringField("dyn2")))))), | |
| Contains(PbField( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| MsgField(Contains(PbField( | |
| perfetto_protos_InternedData_event_names_field_number, | |
| MsgField(Contains( | |
| PbField(perfetto_protos_EventName_name_field_number, | |
| StringField("interned_string")))))))))))))); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlInstant) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event")); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| bool found = false; | |
| for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| found = true; | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_INSTANT))))); | |
| IdFieldView name_iid_fields( | |
| track_event.front(), perfetto_protos_TrackEvent_name_iid_field_number); | |
| ASSERT_THAT(name_iid_fields, ElementsAre(VarIntField(_))); | |
| uint64_t name_iid = name_iid_fields.front().value.integer64; | |
| EXPECT_THAT(trace_field, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_InternedData_event_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_EventName_iid_field_number, | |
| VarIntField(name_iid)), | |
| PbField(perfetto_protos_EventName_name_field_number, | |
| StringField("event"))))))))); | |
| } | |
| EXPECT_TRUE(found); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventLlInstant) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| if (PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT( | |
| cat1.enabled, PERFETTO_MEMORY_ORDER_RELAXED))) { | |
| struct PerfettoTeTimestamp timestamp = PerfettoTeGetTimestamp(); | |
| int32_t type = PERFETTO_TE_TYPE_INSTANT; | |
| const char* name = "event"; | |
| for (struct PerfettoTeLlIterator ctx = | |
| PerfettoTeLlBeginSlowPath(&cat1, timestamp); | |
| ctx.impl.ds.tracer != nullptr; | |
| PerfettoTeLlNext(&cat1, timestamp, &ctx)) { | |
| uint64_t name_iid; | |
| { | |
| struct PerfettoDsRootTracePacket trace_packet; | |
| PerfettoTeLlPacketBegin(&ctx, &trace_packet); | |
| PerfettoTeLlWriteTimestamp(&trace_packet.msg, ×tamp); | |
| perfetto_protos_TracePacket_set_sequence_flags( | |
| &trace_packet.msg, | |
| perfetto_protos_TracePacket_SEQ_NEEDS_INCREMENTAL_STATE); | |
| { | |
| struct PerfettoTeLlInternContext intern_ctx; | |
| PerfettoTeLlInternContextInit(&intern_ctx, ctx.impl.incr, | |
| &trace_packet.msg); | |
| PerfettoTeLlInternRegisteredCat(&intern_ctx, &cat1); | |
| name_iid = PerfettoTeLlInternEventName(&intern_ctx, name); | |
| PerfettoTeLlInternContextDestroy(&intern_ctx); | |
| } | |
| { | |
| struct perfetto_protos_TrackEvent te_msg; | |
| perfetto_protos_TracePacket_begin_track_event(&trace_packet.msg, | |
| &te_msg); | |
| perfetto_protos_TrackEvent_set_type( | |
| &te_msg, static_cast<enum perfetto_protos_TrackEvent_Type>(type)); | |
| PerfettoTeLlWriteRegisteredCat(&te_msg, &cat1); | |
| PerfettoTeLlWriteInternedEventName(&te_msg, name_iid); | |
| perfetto_protos_TracePacket_end_track_event(&trace_packet.msg, | |
| &te_msg); | |
| } | |
| PerfettoTeLlPacketEnd(&ctx, &trace_packet); | |
| } | |
| } | |
| } | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| bool found = false; | |
| for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| found = true; | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_INSTANT))))); | |
| IdFieldView name_iid_fields( | |
| track_event.front(), perfetto_protos_TrackEvent_name_iid_field_number); | |
| ASSERT_THAT(name_iid_fields, ElementsAre(VarIntField(_))); | |
| uint64_t name_iid = name_iid_fields.front().value.integer64; | |
| EXPECT_THAT(trace_field, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_InternedData_event_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_EventName_iid_field_number, | |
| VarIntField(name_iid)), | |
| PbField(perfetto_protos_EventName_name_field_number, | |
| StringField("event"))))))))); | |
| } | |
| EXPECT_TRUE(found); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlInstantNoIntern) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event"), PERFETTO_TE_NO_INTERN()); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| bool found = false; | |
| for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| found = true; | |
| ASSERT_THAT( | |
| track_event, | |
| ElementsAre(AllOf( | |
| AllFieldsWithId(perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_INSTANT))), | |
| AllFieldsWithId(perfetto_protos_TrackEvent_name_field_number, | |
| ElementsAre(StringField("event")))))); | |
| } | |
| EXPECT_TRUE(found); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlDbgArg) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event"), | |
| PERFETTO_TE_ARG_UINT64("arg_name", 42)); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| bool found = false; | |
| for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| found = true; | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_INSTANT))))); | |
| IdFieldView name_iid_fields( | |
| track_event.front(), perfetto_protos_TrackEvent_name_iid_field_number); | |
| ASSERT_THAT(name_iid_fields, ElementsAre(VarIntField(_))); | |
| uint64_t name_iid = name_iid_fields.front().value.integer64; | |
| IdFieldView debug_annot_fields( | |
| track_event.front(), | |
| perfetto_protos_TrackEvent_debug_annotations_field_number); | |
| ASSERT_THAT( | |
| debug_annot_fields, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_DebugAnnotation_name_iid_field_number, | |
| VarIntField(_)), | |
| PbField(perfetto_protos_DebugAnnotation_uint_value_field_number, | |
| VarIntField(42)))))); | |
| uint64_t arg_name_iid = | |
| IdFieldView(debug_annot_fields.front(), | |
| perfetto_protos_DebugAnnotation_name_iid_field_number) | |
| .front() | |
| .value.integer64; | |
| EXPECT_THAT( | |
| trace_field, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| ElementsAre(AllOf( | |
| AllFieldsWithId( | |
| perfetto_protos_InternedData_event_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_EventName_iid_field_number, | |
| VarIntField(name_iid)), | |
| PbField(perfetto_protos_EventName_name_field_number, | |
| StringField("event")))))), | |
| AllFieldsWithId( | |
| perfetto_protos_InternedData_debug_annotation_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField( | |
| perfetto_protos_DebugAnnotationName_iid_field_number, | |
| VarIntField(arg_name_iid)), | |
| PbField( | |
| perfetto_protos_DebugAnnotationName_name_field_number, | |
| StringField("arg_name")))))))))); | |
| } | |
| EXPECT_TRUE(found); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlNamedTrack) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event"), | |
| PERFETTO_TE_NAMED_TRACK("MyTrack", 1, 2)); | |
| uint64_t kExpectedUuid = PerfettoTeNamedTrackUuid("MyTrack", 1, 2); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data), | |
| AllOf( | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_descriptor_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_TrackDescriptor_uuid_field_number, | |
| VarIntField(kExpectedUuid)), | |
| PbField(perfetto_protos_TrackDescriptor_name_field_number, | |
| StringField("MyTrack")), | |
| PbField( | |
| perfetto_protos_TrackDescriptor_parent_uuid_field_number, | |
| VarIntField(2)))))))), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_event_field_number, | |
| ElementsAre(AllOf( | |
| AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_INSTANT))), | |
| AllFieldsWithId( | |
| perfetto_protos_TrackEvent_track_uuid_field_number, | |
| ElementsAre(VarIntField(kExpectedUuid)))))))))); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlRegisteredCounter) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PerfettoTeRegisteredTrack my_counter_track; | |
| PerfettoTeCounterTrackRegister(&my_counter_track, "MyCounter", | |
| PerfettoTeProcessTrackUuid(), true); | |
| PERFETTO_TE(cat1, PERFETTO_TE_COUNTER(), | |
| PERFETTO_TE_REGISTERED_TRACK(&my_counter_track), | |
| PERFETTO_TE_INT_COUNTER(42)); | |
| PerfettoTeRegisteredTrackUnregister(&my_counter_track); | |
| uint64_t kExpectedUuid = | |
| PerfettoTeCounterTrackUuid("MyCounter", PerfettoTeProcessTrackUuid()); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data), | |
| AllOf( | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_descriptor_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_TrackDescriptor_uuid_field_number, | |
| VarIntField(kExpectedUuid)), | |
| PbField( | |
| perfetto_protos_TrackDescriptor_static_name_field_number, | |
| StringField("MyCounter")), | |
| PbField( | |
| perfetto_protos_TrackDescriptor_parent_uuid_field_number, | |
| VarIntField(PerfettoTeProcessTrackUuid())), | |
| PbField( | |
| perfetto_protos_TrackDescriptor_counter_field_number, | |
| MsgField(_)))))))), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_event_field_number, | |
| ElementsAre(AllOf( | |
| AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_COUNTER))), | |
| AllFieldsWithId( | |
| perfetto_protos_TrackEvent_counter_value_field_number, | |
| ElementsAre(VarIntField(42))), | |
| AllFieldsWithId( | |
| perfetto_protos_TrackEvent_track_uuid_field_number, | |
| ElementsAre(VarIntField(kExpectedUuid)))))))))); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlRegisteredState) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PerfettoTeRegisteredTrack my_state_track; | |
| PerfettoTeStateTrackRegister(&my_state_track, "MyState", | |
| PerfettoTeProcessTrackUuid(), true); | |
| PERFETTO_TE(cat1, PERFETTO_TE_STATE("RUNNING"), | |
| PERFETTO_TE_REGISTERED_TRACK(&my_state_track), | |
| PERFETTO_TE_NO_INTERN()); | |
| PerfettoTeRegisteredTrackUnregister(&my_state_track); | |
| uint64_t kExpectedUuid = | |
| PerfettoTeStateTrackUuid("MyState", PerfettoTeProcessTrackUuid()); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data), | |
| AllOf( | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_descriptor_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_TrackDescriptor_uuid_field_number, | |
| VarIntField(kExpectedUuid)), | |
| PbField( | |
| perfetto_protos_TrackDescriptor_static_name_field_number, | |
| StringField("MyState")), | |
| PbField( | |
| perfetto_protos_TrackDescriptor_parent_uuid_field_number, | |
| VarIntField(PerfettoTeProcessTrackUuid())), | |
| PbField( | |
| perfetto_protos_TrackDescriptor_state_field_number, | |
| MsgField(_)))))))), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_event_field_number, | |
| ElementsAre(AllOf( | |
| AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_STATE))), | |
| AllFieldsWithId( | |
| perfetto_protos_TrackEvent_name_field_number, | |
| ElementsAre(StringField("RUNNING"))), | |
| AllFieldsWithId( | |
| perfetto_protos_TrackEvent_track_uuid_field_number, | |
| ElementsAre(VarIntField(kExpectedUuid)))))))))); | |
| } | |
| TEST_F(SharedLibTrackEventTest, Scoped) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| { | |
| PERFETTO_TE_SCOPED(cat1, PERFETTO_TE_SLICE("slice"), | |
| PERFETTO_TE_ARG_UINT64("arg_name", 42)); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event")); | |
| } | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| auto trace_view = FieldView(data); | |
| auto it = trace_view.begin(); | |
| for (; it != trace_view.end(); it++) { | |
| struct PerfettoPbDecoderField trace_field = *it; | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_SLICE_BEGIN))))); | |
| IdFieldView name_iid_fields( | |
| track_event.front(), perfetto_protos_TrackEvent_name_iid_field_number); | |
| ASSERT_THAT(name_iid_fields, ElementsAre(VarIntField(_))); | |
| uint64_t name_iid = name_iid_fields.front().value.integer64; | |
| IdFieldView debug_annot_fields( | |
| track_event.front(), | |
| perfetto_protos_TrackEvent_debug_annotations_field_number); | |
| ASSERT_THAT( | |
| debug_annot_fields, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_DebugAnnotation_name_iid_field_number, | |
| VarIntField(_)), | |
| PbField(perfetto_protos_DebugAnnotation_uint_value_field_number, | |
| VarIntField(42)))))); | |
| uint64_t arg_name_iid = | |
| IdFieldView(debug_annot_fields.front(), | |
| perfetto_protos_DebugAnnotation_name_iid_field_number) | |
| .front() | |
| .value.integer64; | |
| EXPECT_THAT( | |
| trace_field, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| ElementsAre(AllOf( | |
| AllFieldsWithId( | |
| perfetto_protos_InternedData_event_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_EventName_iid_field_number, | |
| VarIntField(name_iid)), | |
| PbField(perfetto_protos_EventName_name_field_number, | |
| StringField("slice")))))), | |
| AllFieldsWithId( | |
| perfetto_protos_InternedData_debug_annotation_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField( | |
| perfetto_protos_DebugAnnotationName_iid_field_number, | |
| VarIntField(arg_name_iid)), | |
| PbField( | |
| perfetto_protos_DebugAnnotationName_name_field_number, | |
| StringField("arg_name")))))))))); | |
| it++; | |
| break; | |
| } | |
| ASSERT_NE(it, trace_view.end()); | |
| for (; it != trace_view.end(); it++) { | |
| struct PerfettoPbDecoderField trace_field = *it; | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_INSTANT))))); | |
| IdFieldView name_iid_fields( | |
| track_event.front(), perfetto_protos_TrackEvent_name_iid_field_number); | |
| ASSERT_THAT(name_iid_fields, ElementsAre(VarIntField(_))); | |
| uint64_t name_iid = name_iid_fields.front().value.integer64; | |
| EXPECT_THAT(trace_field, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_InternedData_event_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_EventName_iid_field_number, | |
| VarIntField(name_iid)), | |
| PbField(perfetto_protos_EventName_name_field_number, | |
| StringField("event"))))))))); | |
| it++; | |
| break; | |
| } | |
| ASSERT_NE(it, trace_view.end()); | |
| for (; it != trace_view.end(); it++) { | |
| struct PerfettoPbDecoderField trace_field = *it; | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_SLICE_END))))); | |
| IdFieldView debug_annot_fields( | |
| track_event.front(), | |
| perfetto_protos_TrackEvent_debug_annotations_field_number); | |
| ASSERT_THAT(debug_annot_fields, ElementsAre()); | |
| it++; | |
| break; | |
| } | |
| } | |
| TEST_F(SharedLibTrackEventTest, ScopedDisabled) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_disabled_category("cat1") | |
| .Build(); | |
| // Check that the PERFETTO_TE_SCOPED macro does not have any effect if the | |
| // category is disabled. | |
| { | |
| PERFETTO_TE_SCOPED(cat1, PERFETTO_TE_SLICE("slice")); | |
| } | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| auto trace_view = FieldView(data); | |
| auto it = trace_view.begin(); | |
| for (; it != trace_view.end(); it++) { | |
| struct PerfettoPbDecoderField trace_field = *it; | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| ASSERT_EQ(track_event.size(), 0u); | |
| } | |
| } | |
| TEST_F(SharedLibTrackEventTest, ScopedSingleLine) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| // Check that the PERFETTO_TE_SCOPED macro is expanded into a single | |
| // statement. Emitting the end event should not escape | |
| if (false) | |
| PERFETTO_TE_SCOPED(cat1, PERFETTO_TE_SLICE("slice")); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| auto trace_view = FieldView(data); | |
| auto it = trace_view.begin(); | |
| for (; it != trace_view.end(); it++) { | |
| struct PerfettoPbDecoderField trace_field = *it; | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| ASSERT_EQ(track_event.size(), 0u); | |
| } | |
| } | |
| TEST_F(SharedLibTrackEventTest, ScopedCapture) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| // Check that the PERFETTO_TE_SCOPED macro can capture variables. | |
| uint64_t value = 42; | |
| { | |
| PERFETTO_TE_SCOPED(cat1, PERFETTO_TE_SLICE("slice"), | |
| PERFETTO_TE_ARG_UINT64("arg_name", value)); | |
| } | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| auto trace_view = FieldView(data); | |
| auto it = trace_view.begin(); | |
| for (; it != trace_view.end(); it++) { | |
| struct PerfettoPbDecoderField trace_field = *it; | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_SLICE_BEGIN))))); | |
| IdFieldView name_iid_fields( | |
| track_event.front(), perfetto_protos_TrackEvent_name_iid_field_number); | |
| ASSERT_THAT(name_iid_fields, ElementsAre(VarIntField(_))); | |
| uint64_t name_iid = name_iid_fields.front().value.integer64; | |
| IdFieldView debug_annot_fields( | |
| track_event.front(), | |
| perfetto_protos_TrackEvent_debug_annotations_field_number); | |
| ASSERT_THAT( | |
| debug_annot_fields, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_DebugAnnotation_name_iid_field_number, | |
| VarIntField(_)), | |
| PbField(perfetto_protos_DebugAnnotation_uint_value_field_number, | |
| VarIntField(42)))))); | |
| uint64_t arg_name_iid = | |
| IdFieldView(debug_annot_fields.front(), | |
| perfetto_protos_DebugAnnotation_name_iid_field_number) | |
| .front() | |
| .value.integer64; | |
| EXPECT_THAT( | |
| trace_field, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| ElementsAre(AllOf( | |
| AllFieldsWithId( | |
| perfetto_protos_InternedData_event_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_EventName_iid_field_number, | |
| VarIntField(name_iid)), | |
| PbField(perfetto_protos_EventName_name_field_number, | |
| StringField("slice")))))), | |
| AllFieldsWithId( | |
| perfetto_protos_InternedData_debug_annotation_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField( | |
| perfetto_protos_DebugAnnotationName_iid_field_number, | |
| VarIntField(arg_name_iid)), | |
| PbField( | |
| perfetto_protos_DebugAnnotationName_name_field_number, | |
| StringField("arg_name")))))))))); | |
| it++; | |
| break; | |
| } | |
| ASSERT_NE(it, trace_view.end()); | |
| for (; it != trace_view.end(); it++) { | |
| struct PerfettoPbDecoderField trace_field = *it; | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_SLICE_END))))); | |
| IdFieldView debug_annot_fields( | |
| track_event.front(), | |
| perfetto_protos_TrackEvent_debug_annotations_field_number); | |
| ASSERT_THAT(debug_annot_fields, ElementsAre()); | |
| it++; | |
| break; | |
| } | |
| } | |
| TEST_F(SharedLibTrackEventTest, ScopedFunc) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| // Check that using __func__ works as expected. | |
| { | |
| PERFETTO_TE_SCOPED(cat1, PERFETTO_TE_SLICE(__func__)); | |
| } | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| auto trace_view = FieldView(data); | |
| auto it = trace_view.begin(); | |
| for (; it != trace_view.end(); it++) { | |
| struct PerfettoPbDecoderField trace_field = *it; | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_SLICE_BEGIN))))); | |
| IdFieldView name_iid_fields( | |
| track_event.front(), perfetto_protos_TrackEvent_name_iid_field_number); | |
| ASSERT_THAT(name_iid_fields, ElementsAre(VarIntField(_))); | |
| uint64_t name_iid = name_iid_fields.front().value.integer64; | |
| EXPECT_THAT(trace_field, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_interned_data_field_number, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_InternedData_event_names_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_EventName_iid_field_number, | |
| VarIntField(name_iid)), | |
| PbField(perfetto_protos_EventName_name_field_number, | |
| StringField(__func__))))))))); | |
| it++; | |
| break; | |
| } | |
| ASSERT_NE(it, trace_view.end()); | |
| for (; it != trace_view.end(); it++) { | |
| struct PerfettoPbDecoderField trace_field = *it; | |
| ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| IdFieldView track_event( | |
| trace_field, perfetto_protos_TracePacket_track_event_field_number); | |
| if (track_event.size() == 0) { | |
| continue; | |
| } | |
| ASSERT_THAT(track_event, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_type_field_number, | |
| ElementsAre(VarIntField( | |
| perfetto_protos_TrackEvent_TYPE_SLICE_END))))); | |
| IdFieldView debug_annot_fields( | |
| track_event.front(), | |
| perfetto_protos_TrackEvent_debug_annotations_field_number); | |
| ASSERT_THAT(debug_annot_fields, ElementsAre()); | |
| it++; | |
| break; | |
| } | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlProtoFieldString) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PERFETTO_TE( | |
| cat1, PERFETTO_TE_INSTANT("event"), | |
| PERFETTO_TE_PROTO_FIELDS(PERFETTO_TE_PROTO_FIELD_NESTED( | |
| perfetto_protos_TrackEvent_debug_annotations_field_number, | |
| PERFETTO_TE_PROTO_FIELD_CSTR( | |
| perfetto_protos_DebugAnnotation_name_field_number, "name"), | |
| PERFETTO_TE_PROTO_FIELD_VARINT( | |
| perfetto_protos_DebugAnnotation_uint_value_field_number, 42)))); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_event_field_number, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_debug_annotations_field_number, | |
| ElementsAre(MsgField(UnorderedElementsAre( | |
| PbField(perfetto_protos_DebugAnnotation_name_field_number, | |
| StringField("name")), | |
| PbField( | |
| perfetto_protos_DebugAnnotation_uint_value_field_number, | |
| VarIntField(42))))))))))); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlNestedTrack) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PerfettoTeRegisteredTrack my_named_track; | |
| PerfettoTeNamedTrackRegister(&my_named_track, "registered_track1", 0, | |
| PerfettoTeProcessTrackUuid(), false); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event1"), | |
| PERFETTO_TE_NESTED_TRACKS( | |
| PERFETTO_TE_NESTED_TRACK_PROCESS(), | |
| PERFETTO_TE_NESTED_TRACK_NAMED("track_name1", 1))); | |
| PERFETTO_TE(cat1, PERFETTO_TE_COUNTER(), | |
| PERFETTO_TE_NESTED_TRACKS( | |
| PERFETTO_TE_NESTED_TRACK_REGISTERED(&my_named_track), | |
| PERFETTO_TE_NESTED_TRACK_COUNTER("counter_name")), | |
| PERFETTO_TE_INT_COUNTER(42)); | |
| PerfettoTeRegisteredTrackUnregister(&my_named_track); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| std::optional<uint64_t> instant_track_uuid = 0; | |
| std::optional<uint64_t> counter_track_uuid = 0; | |
| std::optional<uint64_t> track_name1_uuid = 0; | |
| std::optional<uint64_t> track_name1_parent_uuid = 0; | |
| std::optional<uint64_t> process_uuid = 0; | |
| std::optional<uint64_t> registered_track_uuid = 0; | |
| std::optional<uint64_t> counter_uuid = 0; | |
| std::optional<uint64_t> counter_parent_uuid = 0; | |
| auto trace_view = FieldView(data); | |
| for (auto trace_field = trace_view.begin(); trace_field != trace_view.end(); | |
| trace_field++) { | |
| ASSERT_THAT(*trace_field, PbField(perfetto_protos_Trace_packet_field_number, | |
| MsgField(_))); | |
| auto packet_view = FieldView(*trace_field); | |
| for (auto it = packet_view.begin(); it != packet_view.end(); it++) { | |
| struct PerfettoPbDecoderField packet_field = *it; | |
| if (packet_field.id == | |
| perfetto_protos_TracePacket_track_event_field_number) { | |
| ASSERT_THAT( | |
| packet_field, | |
| PbField(perfetto_protos_TracePacket_track_event_field_number, | |
| MsgField(_))); | |
| IdFieldView track_uuid_field( | |
| packet_field, perfetto_protos_TrackEvent_track_uuid_field_number); | |
| ASSERT_THAT(track_uuid_field, ElementsAre(VarIntField(_))); | |
| IdFieldView type_field(packet_field, | |
| perfetto_protos_TrackEvent_type_field_number); | |
| ASSERT_THAT(type_field, ElementsAre(VarIntField(_))); | |
| if (type_field.front().value.integer64 == | |
| perfetto_protos_TrackEvent_TYPE_COUNTER) { | |
| counter_track_uuid = track_uuid_field.front().value.integer64; | |
| } else if (type_field.front().value.integer64 == | |
| perfetto_protos_TrackEvent_TYPE_INSTANT) { | |
| instant_track_uuid = track_uuid_field.front().value.integer64; | |
| } | |
| } else if (packet_field.id == | |
| perfetto_protos_TracePacket_track_descriptor_field_number) { | |
| ASSERT_THAT( | |
| packet_field, | |
| PbField(perfetto_protos_TracePacket_track_descriptor_field_number, | |
| MsgField(_))); | |
| IdFieldView uuid_field( | |
| packet_field, perfetto_protos_TrackDescriptor_uuid_field_number); | |
| ASSERT_THAT(uuid_field, ElementsAre(VarIntField(_))); | |
| IdFieldView process_field( | |
| packet_field, perfetto_protos_TrackDescriptor_process_field_number); | |
| IdFieldView counter_field( | |
| packet_field, perfetto_protos_TrackDescriptor_counter_field_number); | |
| IdFieldView name_field( | |
| packet_field, perfetto_protos_TrackDescriptor_name_field_number); | |
| IdFieldView static_name_field( | |
| packet_field, | |
| perfetto_protos_TrackDescriptor_static_name_field_number); | |
| IdFieldView parent_uuid_field( | |
| packet_field, | |
| perfetto_protos_TrackDescriptor_parent_uuid_field_number); | |
| if (process_field.size() == 1) { | |
| process_uuid = uuid_field.front().value.integer64; | |
| } else if (counter_field.size() == 1) { | |
| ASSERT_THAT(parent_uuid_field, ElementsAre(VarIntField(_))); | |
| counter_uuid = uuid_field.front().value.integer64; | |
| counter_parent_uuid = parent_uuid_field.front().value.integer64; | |
| } else if (name_field.size() == 1 || static_name_field.size() == 1) { | |
| auto& field = name_field.size() == 1 ? name_field : static_name_field; | |
| ASSERT_THAT(parent_uuid_field, ElementsAre(VarIntField(_))); | |
| ASSERT_THAT(field.front(), StringField(_)); | |
| std::string_view name(reinterpret_cast<const char*>( | |
| field.front().value.delimited.start), | |
| field.front().value.delimited.len); | |
| if (name == "track_name1") { | |
| track_name1_uuid = uuid_field.front().value.integer64; | |
| track_name1_parent_uuid = parent_uuid_field.front().value.integer64; | |
| } else if (name == "registered_track1") { | |
| registered_track_uuid = uuid_field.front().value.integer64; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| EXPECT_NE(instant_track_uuid, std::nullopt); | |
| EXPECT_NE(track_name1_parent_uuid, std::nullopt); | |
| EXPECT_NE(counter_track_uuid, std::nullopt); | |
| EXPECT_NE(counter_parent_uuid, std::nullopt); | |
| EXPECT_EQ(instant_track_uuid, track_name1_uuid); | |
| EXPECT_EQ(track_name1_parent_uuid, process_uuid); | |
| EXPECT_EQ(counter_track_uuid, counter_uuid); | |
| EXPECT_EQ(counter_parent_uuid, registered_track_uuid); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlNestedTrackOrdered) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event"), | |
| PERFETTO_TE_NESTED_TRACKS( | |
| PERFETTO_TE_NESTED_TRACK_PROCESS(), | |
| PERFETTO_TE_NESTED_TRACK_NAMED_ORDERED( | |
| "parent", 0, 0, PERFETTO_TE_HL_CHILD_ORDERING_EXPLICIT), | |
| PERFETTO_TE_NESTED_TRACK_NAMED_ORDERED("child", 0, 7, 0))); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| // The parent track descriptor declares explicit child ordering. | |
| EXPECT_THAT( | |
| FieldView(data), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_descriptor_field_number, | |
| ElementsAre(MsgField(AllOf( | |
| Contains( | |
| PbField(perfetto_protos_TrackDescriptor_name_field_number, | |
| StringField("parent"))), | |
| Contains(PbField( | |
| perfetto_protos_TrackDescriptor_child_ordering_field_number, | |
| VarIntField( | |
| perfetto_protos_TrackDescriptor_EXPLICIT)))))))))); | |
| // The child track descriptor carries its sibling order rank. | |
| EXPECT_THAT( | |
| FieldView(data), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_descriptor_field_number, | |
| ElementsAre(MsgField(AllOf( | |
| Contains( | |
| PbField(perfetto_protos_TrackDescriptor_name_field_number, | |
| StringField("child"))), | |
| Contains(PbField( | |
| perfetto_protos_TrackDescriptor_sibling_order_rank_field_number, | |
| VarIntField(7)))))))))); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlNestedTrackMerged) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PERFETTO_TE( | |
| cat1, PERFETTO_TE_INSTANT("event1"), | |
| PERFETTO_TE_NESTED_TRACKS( | |
| PERFETTO_TE_NESTED_TRACK_PROCESS(), | |
| PERFETTO_TE_NESTED_TRACK_NAMED_MERGED( | |
| "str_keyed", 0, | |
| PERFETTO_TE_HL_SIBLING_MERGE_BEHAVIOR_BY_SIBLING_MERGE_KEY, | |
| "merge_group_a", 0))); | |
| PERFETTO_TE( | |
| cat1, PERFETTO_TE_INSTANT("event2"), | |
| PERFETTO_TE_NESTED_TRACKS( | |
| PERFETTO_TE_NESTED_TRACK_PROCESS(), | |
| PERFETTO_TE_NESTED_TRACK_NAMED_MERGED( | |
| "int_keyed", 1, | |
| PERFETTO_TE_HL_SIBLING_MERGE_BEHAVIOR_BY_SIBLING_MERGE_KEY, | |
| PERFETTO_NULL, 42))); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| // The string-keyed track carries the behavior and the string key. | |
| EXPECT_THAT( | |
| FieldView(data), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_descriptor_field_number, | |
| ElementsAre(MsgField(AllOf( | |
| Contains( | |
| PbField(perfetto_protos_TrackDescriptor_name_field_number, | |
| StringField("str_keyed"))), | |
| Contains(PbField( | |
| perfetto_protos_TrackDescriptor_sibling_merge_behavior_field_number, | |
| VarIntField( | |
| perfetto_protos_TrackDescriptor_SIBLING_MERGE_BEHAVIOR_BY_SIBLING_MERGE_KEY))), | |
| Contains(PbField( | |
| perfetto_protos_TrackDescriptor_sibling_merge_key_field_number, | |
| StringField("merge_group_a")))))))))); | |
| // The integer-keyed track carries the behavior and the integer key. | |
| EXPECT_THAT( | |
| FieldView(data), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_descriptor_field_number, | |
| ElementsAre(MsgField(AllOf( | |
| Contains( | |
| PbField(perfetto_protos_TrackDescriptor_name_field_number, | |
| StringField("int_keyed"))), | |
| Contains(PbField( | |
| perfetto_protos_TrackDescriptor_sibling_merge_behavior_field_number, | |
| VarIntField( | |
| perfetto_protos_TrackDescriptor_SIBLING_MERGE_BEHAVIOR_BY_SIBLING_MERGE_KEY))), | |
| Contains(PbField( | |
| perfetto_protos_TrackDescriptor_sibling_merge_key_int_field_number, | |
| VarIntField(42)))))))))); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventHlCorrelationId) { | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("*") | |
| .Build(); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event1"), | |
| PERFETTO_TE_CORRELATION_ID(1234)); | |
| PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event2"), | |
| PERFETTO_TE_CORRELATION_ID_STR("req-5678")); | |
| tracing_session.StopBlocking(); | |
| std::vector<uint8_t> data = tracing_session.ReadBlocking(); | |
| EXPECT_THAT( | |
| FieldView(data), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_event_field_number, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_correlation_id_field_number, | |
| ElementsAre(VarIntField(1234)))))))); | |
| EXPECT_THAT( | |
| FieldView(data), | |
| Contains(PbField( | |
| perfetto_protos_Trace_packet_field_number, | |
| AllFieldsWithId( | |
| perfetto_protos_TracePacket_track_event_field_number, | |
| ElementsAre(AllFieldsWithId( | |
| perfetto_protos_TrackEvent_correlation_id_str_field_number, | |
| ElementsAre(StringField("req-5678")))))))); | |
| } | |
| TEST_F(SharedLibTrackEventTest, TrackEventIsCategoryEnabled) { | |
| ASSERT_FALSE(PERFETTO_TE_IS_CATEGORY_ENABLED(cat1)); | |
| TracingSession tracing_session = TracingSession::Builder() | |
| .set_data_source_name("track_event") | |
| .add_enabled_category("cat1") | |
| .add_disabled_category("*") | |
| .Build(); | |
| EXPECT_TRUE(PERFETTO_TE_IS_CATEGORY_ENABLED(cat1)); | |
| EXPECT_FALSE(PERFETTO_TE_IS_CATEGORY_ENABLED(cat2)); | |
| tracing_session.StopBlocking(); | |
| EXPECT_FALSE(PERFETTO_TE_IS_CATEGORY_ENABLED(cat1)); | |
| } | |
| } // namespace |