| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/fxt/serializer.h> |
| |
| #include <gtest/gtest.h> |
| |
| #include "lib/fxt/fields.h" |
| #include "lib/fxt/record_types.h" |
| #include "zircon/syscalls/object.h" |
| #include "zircon/types.h" |
| |
| namespace { |
| struct FakeRecord { |
| void WriteWord(uint64_t word) { |
| uint8_t* bytes = reinterpret_cast<uint8_t*>(&word); |
| for (unsigned i = 0; i < 8; i++) { |
| bytes_.push_back(bytes[i]); |
| } |
| } |
| void WriteBytes(const void* buffer, size_t num_bytes) { |
| for (unsigned i = 0; i < num_bytes; i++) { |
| bytes_.push_back(reinterpret_cast<const uint8_t*>(buffer)[i]); |
| } |
| |
| // 0 pad the buffer to an 8 byte boundary |
| for (size_t i = num_bytes; i % 8 != 0; i++) { |
| bytes_.push_back(0); |
| } |
| } |
| void Commit() { |
| // Records must only be committed once |
| EXPECT_FALSE(committed); |
| committed = true; |
| } |
| |
| bool committed = false; |
| std::vector<uint8_t>& bytes_; |
| }; |
| |
| struct FakeWriter { |
| using Reservation = FakeRecord; |
| std::vector<uint8_t> bytes; |
| zx::status<FakeRecord> Reserve(uint64_t header) { |
| FakeRecord rec{false, bytes}; |
| rec.WriteWord(header); |
| return zx::ok(rec); |
| } |
| }; |
| |
| struct FakeNoMemWriter { |
| using Reservation = FakeRecord; |
| zx::status<FakeRecord> Reserve(uint64_t header) { return zx::error(ZX_ERR_NO_MEMORY); } |
| }; |
| |
| TEST(Serializer, NoMemWriter) { |
| FakeNoMemWriter writer_no_mem; |
| EXPECT_EQ(ZX_ERR_NO_MEMORY, fxt::WriteInitializationRecord(&writer_no_mem, 0xABCD)); |
| } |
| |
| TEST(Serializer, ProviderInfoMetadataRecord) { |
| FakeWriter writer_success; |
| uint32_t provider_id = 0xAABBCCDD; |
| const char* provider_name = "test_provider"; |
| size_t provider_name_length = strlen(provider_name); |
| EXPECT_EQ(ZX_OK, fxt::WriteProviderInfoMetadataRecord(&writer_success, provider_id, provider_name, |
| provider_name_length)); |
| // 1 word header, 2 words name stream |
| EXPECT_EQ(writer_success.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer_success.bytes.data()); |
| |
| uint64_t header = bytes[0]; |
| // Record type of 0 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'0000}); |
| // 3 words in size |
| EXPECT_EQ(header & 0x0000'0000'0000'FFF0, uint64_t{0x0000'0000'0000'0030}); |
| // Metadata type 1 |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0001'0000}); |
| // Provider id |
| EXPECT_EQ(header & 0x000F'FFFF'FFF0'0000, uint64_t{0x000A'ABBC'CDD0'0000}); |
| // Name length |
| EXPECT_EQ(header & 0x0FF0'0000'0000'0000, uint64_t{0x00D0'0000'0000'0000}); |
| EXPECT_EQ(std::memcmp(bytes + 1, "test_pro", 8), 0); |
| EXPECT_EQ(std::memcmp(bytes + 2, "vider\0\0\0", 8), 0); |
| } |
| |
| TEST(Serializer, ProviderSectionMetadataRecord) { |
| FakeWriter writer_success; |
| uint32_t provider_id = 0xAABBCCDD; |
| EXPECT_EQ(ZX_OK, fxt::WriteProviderSectionMetadataRecord(&writer_success, provider_id)); |
| // 1 word header |
| EXPECT_EQ(writer_success.bytes.size(), fxt::WordSize(1).SizeInBytes()); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer_success.bytes.data()); |
| |
| uint64_t header = bytes[0]; |
| // Record type of 0 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'0000}); |
| // 1 words in size |
| EXPECT_EQ(header & 0x0000'0000'0000'FFF0, uint64_t{0x0000'0000'0000'0010}); |
| // Metadata type 2 |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0002'0000}); |
| // Provider id |
| EXPECT_EQ(header & 0x000F'FFFF'FFF0'0000, uint64_t{0x000A'ABBC'CDD0'0000}); |
| } |
| |
| TEST(Serializer, ProviderEventMetadataRecord) { |
| FakeWriter writer_success; |
| uint32_t provider_id = 0xAABBCCDD; |
| uint8_t event_id = 0x7; |
| EXPECT_EQ(ZX_OK, fxt::WriteProviderEventMetadataRecord(&writer_success, provider_id, event_id)); |
| // 1 word header |
| EXPECT_EQ(writer_success.bytes.size(), fxt::WordSize(1).SizeInBytes()); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer_success.bytes.data()); |
| |
| uint64_t header = bytes[0]; |
| // Record type of 0 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'0000}); |
| // 1 words in size |
| EXPECT_EQ(header & 0x0000'0000'0000'FFF0, uint64_t{0x0000'0000'0000'0010}); |
| // Metadata type 3 |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0003'0000}); |
| // Provider id |
| EXPECT_EQ(header & 0x000F'FFFF'FFF0'0000, uint64_t{0x000A'ABBC'CDD0'0000}); |
| // Event Id |
| EXPECT_EQ(header & 0x00F0'0000'0000'0000, uint64_t{0x0070'0000'0000'0000}); |
| } |
| |
| TEST(Serializer, MagicNumberMetadataRecord) { |
| FakeWriter writer_success; |
| EXPECT_EQ(ZX_OK, fxt::WriteMagicNumberRecord(&writer_success)); |
| // 1 word header |
| EXPECT_EQ(writer_success.bytes.size(), fxt::WordSize(1).SizeInBytes()); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer_success.bytes.data()); |
| |
| uint64_t header = bytes[0]; |
| // Record type of 0 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'0000}); |
| // 1 words in size |
| EXPECT_EQ(header & 0x0000'0000'0000'FFF0, uint64_t{0x0000'0000'0000'0010}); |
| // Metadata type 4 |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0004'0000}); |
| // Trace type info 0 |
| EXPECT_EQ(header & 0x0000'0000'00F0'0000, uint64_t{0x0000'0000'0000'0000}); |
| // FxT\16 in little endian |
| EXPECT_EQ(header & 0x00FF'FFFF'FF00'0000, uint64_t{0x0016'5478'4600'0000}); |
| // Remainder is 0 |
| EXPECT_EQ(header & 0xFF00'0000'0000'0000, uint64_t{0x0000'0000'0000'0000}); |
| } |
| |
| TEST(Serializer, InitRecord) { |
| FakeWriter writer_success; |
| EXPECT_EQ(ZX_OK, fxt::WriteInitializationRecord(&writer_success, 0xABCD)); |
| EXPECT_EQ(writer_success.bytes.size(), 16U); |
| |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer_success.bytes.data()); |
| |
| // We expect to see: |
| // Word 0: |
| // Bits [0 .. 3]: The record type (1) |
| // Bits [4 .. 15]: The record type size in 64bit words (2) |
| // Word 1: |
| // The number of ticks per second |
| EXPECT_EQ(bytes[0], uint64_t{0x0000'0000'0000'0021}); |
| EXPECT_EQ(bytes[1], uint64_t{0x0000'0000'0000'ABCD}); |
| } |
| |
| TEST(Serializer, IndexedStringReferences) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, |
| fxt::WriteInstantEventRecord(&writer, event_time, thread_ref, category_ref, name_ref)); |
| // Everything should be a reference, so we should only see two words |
| EXPECT_EQ(writer.bytes.size(), 16U); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = bytes[0]; |
| // We expect to see our string references: |
| // [48 .. 63]: name (string ref) |
| EXPECT_EQ(header & 0xFFFF'0000'0000'0000, uint64_t{0x1234'0000'0000'0000}); |
| // [32 .. 47]: category (string ref) |
| EXPECT_EQ(header & 0x0000'FFFF'0000'0000, uint64_t{0x0000'7777'0000'0000}); |
| } |
| |
| TEST(Serializer, InlineStringReferences) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_inline("category"); |
| fxt::StringRef name_inline("name longer than eight bytes"); |
| |
| FakeWriter inline_writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteInstantEventRecord(&inline_writer, event_time, thread_ref, |
| category_inline, name_inline)); |
| // Everything should be inline, so we should see two words for the header and |
| // timestamp, plus 1 word for "category", plus 4 words for "name longer than |
| // eight bytes". |
| EXPECT_EQ(inline_writer.bytes.size(), fxt::WordSize(7).SizeInBytes()); |
| uint64_t* inline_bytes = reinterpret_cast<uint64_t*>(inline_writer.bytes.data()); |
| uint64_t inline_header = inline_bytes[0]; |
| // We expect our header to indicate inline stringrefs (msb of 1, lower 15 bits denote length) |
| // [48 .. 63]: name (string ref) |
| EXPECT_EQ(inline_header & 0xFFFF'0000'0000'0000, uint64_t{0x801c'0000'0000'0000}); |
| // [32 .. 47]: category (string ref) |
| EXPECT_EQ(inline_header & 0x0000'FFFF'0000'0000, uint64_t{0x0000'8008'0000'0000}); |
| |
| char* category_stream = reinterpret_cast<char*>(inline_bytes + 2); |
| EXPECT_EQ(std::memcmp(category_stream, "category", 8), 0); |
| char* name_stream1 = reinterpret_cast<char*>(inline_bytes + 3); |
| EXPECT_EQ(std::memcmp(name_stream1, "name longer than eight bytes\0\0\0\0", 32), 0); |
| } |
| |
| TEST(Serializer, IndexThreadReferences) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef index_thread_ref(0xAB); |
| fxt::StringRef category(1); |
| fxt::StringRef name(2); |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, |
| fxt::WriteInstantEventRecord(&writer, event_time, index_thread_ref, category, name)); |
| // Everything should be indexed, so we should see two words for the header and |
| // timestamp |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(2).SizeInBytes()); |
| uint64_t* index_bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t index_header = index_bytes[0]; |
| |
| // We expect our header to contain our threadref |
| // [24 .. 31]: thread (thread ref) |
| EXPECT_EQ(index_header & 0x0000'0000'FF00'0000, uint64_t{0x0000'0000'AB00'0000}); |
| } |
| |
| TEST(Serializer, InlineThreadReferences) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef inline_thread_ref(0xDEADBEEF, 0xCAFEF00D); |
| fxt::StringRef category(1); |
| fxt::StringRef name(2); |
| |
| FakeWriter inline_writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteInstantEventRecord(&inline_writer, event_time, inline_thread_ref, |
| category, name)); |
| |
| // We should see two extra words to include the 2 koids |
| EXPECT_EQ(inline_writer.bytes.size(), fxt::WordSize(4).SizeInBytes()); |
| |
| uint64_t* inline_bytes = reinterpret_cast<uint64_t*>(inline_writer.bytes.data()); |
| uint64_t inline_header = inline_bytes[0]; |
| |
| // We expect our header to indicate an inline threadref (all zeros) |
| // [24 .. 31]: thread (thread ref) |
| EXPECT_EQ(inline_header & 0x0000'0000'FF00'0000, uint64_t{0x0000'0000'0000'0000}); |
| |
| // We should see 2 extra words for the inline threadref |
| uint64_t pid = inline_bytes[2]; |
| uint64_t tid = inline_bytes[3]; |
| EXPECT_EQ(pid, 0xDEADBEEF); |
| EXPECT_EQ(tid, 0xCAFEF00D); |
| } |
| |
| TEST(Serializer, IndexedArgumentNames) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category(2); |
| fxt::StringRef name(3); |
| fxt::StringRef arg_name(0x7FFF); |
| |
| FakeWriter indexed_writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteInstantEventRecord(&indexed_writer, event_time, thread_ref, category, |
| name, fxt::Argument(arg_name))); |
| |
| // We should see one extra word for the argument header |
| EXPECT_EQ(indexed_writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* indexed_bytes = reinterpret_cast<uint64_t*>(indexed_writer.bytes.data()); |
| uint64_t indexed_arg_header = indexed_bytes[2]; |
| |
| // We expect our arg header to indicate an indexed stringref |
| // [16 .. 31]: name (string ref) |
| EXPECT_EQ(indexed_arg_header & 0x0000'0000'FFFF'0000, uint64_t{0x0000'0000'7FFF'0000}); |
| } |
| |
| TEST(Serializer, InlineArgumentNames) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category(2); |
| fxt::StringRef name(3); |
| fxt::StringRef arg_name_inline("argname"); |
| FakeWriter inline_writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteInstantEventRecord(&inline_writer, event_time, thread_ref, category, |
| name, fxt::Argument(arg_name_inline))); |
| |
| // We should see one extra word for the argument header, and 1 for the inline string |
| EXPECT_EQ(inline_writer.bytes.size(), fxt::WordSize(4).SizeInBytes()); |
| uint64_t* inline_bytes = reinterpret_cast<uint64_t*>(inline_writer.bytes.data()); |
| uint64_t inline_arg_header = inline_bytes[2]; |
| |
| // We expect our arg header to indicate an inline stringref of length 7 |
| // [16 .. 31]: name (string ref) |
| EXPECT_EQ(inline_arg_header & 0x0000'0000'FFFF'0000, uint64_t{0x0000'0000'8007'0000}); |
| |
| char* name_stream = reinterpret_cast<char*>(inline_bytes + 3); |
| EXPECT_EQ(std::memcmp(name_stream, "argname\0", 8), 0); |
| } |
| |
| TEST(Serializer, Arguments) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category(2); |
| fxt::StringRef name(3); |
| |
| fxt::StringRef arg_name(0x7FFF); |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteInstantEventRecord( |
| &writer, event_time, thread_ref, category, name, fxt::Argument(arg_name), |
| fxt::Argument(arg_name, true), fxt::Argument(arg_name, int32_t{0x12345678}), |
| fxt::Argument(arg_name, uint32_t{0x567890AB}), |
| fxt::Argument(arg_name, int64_t{0x1234'5678'90AB'CDEF}), |
| fxt::Argument<fxt::ArgumentType::kUint64, fxt::RefType::kId>( |
| arg_name, uint64_t{0xFEDC'BA09'8765'4321}), |
| fxt::Argument(arg_name, double{1234.5678}), |
| fxt::Argument<fxt::ArgumentType::kPointer, fxt::RefType::kId>( |
| arg_name, uintptr_t{0xDEADBEEF}), |
| fxt::Argument<fxt::ArgumentType::kKoid, fxt::RefType::kId>( |
| arg_name, zx_koid_t{0x12345678}), |
| fxt::Argument(arg_name, fxt::StringRef(11)))); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| // We should have 10 arguments |
| uint64_t header = bytes[0]; |
| EXPECT_EQ(header & 0x0000'0000'00F0'0000, uint64_t{0x0000'0000'00A0'0000}); |
| |
| const size_t num_words = |
| 1 // header |
| + 1 // time stamp |
| + 5 // 1 word for args that fit in the header (null, bool, int32, uint32, string arg (ref) |
| + (2 * 5); // 2 words for args that don't fit (int64, uint64, double, pointer, koid) |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(num_words).SizeInBytes()); |
| uint64_t null_arg_header = bytes[2]; |
| EXPECT_EQ(null_arg_header, uint64_t{0x0000'0000'7FFF'0010}); |
| |
| uint64_t bool_arg_header = bytes[3]; |
| EXPECT_EQ(bool_arg_header, uint64_t{0x0000'0001'7FFF'0019}); |
| |
| uint64_t int32_arg_header = bytes[4]; |
| EXPECT_EQ(int32_arg_header, uint64_t{0x1234'5678'7FFF'0011}); |
| |
| uint64_t uint32_arg_header = bytes[5]; |
| EXPECT_EQ(uint32_arg_header, uint64_t{0x5678'90AB'7FFF'0012}); |
| |
| uint64_t int64_arg_header = bytes[6]; |
| EXPECT_EQ(int64_arg_header, uint64_t{0x0000'0000'7FFF'0023}); |
| EXPECT_EQ(bytes[7], uint64_t{0x1234'5678'90AB'CDEF}); |
| |
| uint64_t uint64_arg_header = bytes[8]; |
| EXPECT_EQ(uint64_arg_header, uint64_t{0x0000'0000'7FFF'0024}); |
| EXPECT_EQ(bytes[9], uint64_t{0xFEDC'BA09'8765'4321}); |
| |
| uint64_t double_arg_header = bytes[10]; |
| EXPECT_EQ(double_arg_header, uint64_t{0x0000'0000'7FFF'0025}); |
| double exp_double_val = 1234.5678; |
| EXPECT_EQ(bytes[11], *reinterpret_cast<uint64_t*>(&exp_double_val)); |
| |
| uint64_t pointer_arg_header = bytes[12]; |
| EXPECT_EQ(pointer_arg_header, uint64_t{0x0000'0000'7FFF'0027}); |
| EXPECT_EQ(bytes[13], uint64_t{0xDEADBEEF}); |
| |
| uint64_t koid_arg_header = bytes[14]; |
| EXPECT_EQ(koid_arg_header, uint64_t{0x0000'0000'7FFF'0028}); |
| EXPECT_EQ(bytes[15], uint64_t{0x12345678}); |
| |
| uint64_t string_arg_header = bytes[16]; |
| EXPECT_EQ(string_arg_header, uint64_t{0x0000'000B'7FFF'0016}); |
| } |
| |
| TEST(Serializer, InstantEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, |
| fxt::WriteInstantEventRecord(&writer, event_time, thread_ref, category_ref, name_ref)); |
| // One word for the header, one for the timestamp |
| EXPECT_EQ(writer.bytes.size(), 16U); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = bytes[0]; |
| // Event type should be 0 |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0000'0000}); |
| // Timestamp should be correct |
| EXPECT_EQ(bytes[1], event_time); |
| } |
| |
| TEST(Serializer, CounterEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| fxt::StringRef arg_name(0x2345); |
| |
| FakeWriter writer; |
| uint64_t counter_id = 0x334455'667788; |
| EXPECT_EQ(ZX_OK, |
| fxt::WriteCounterEventRecord(&writer, event_time, thread_ref, category_ref, name_ref, |
| counter_id, fxt::Argument(arg_name, true))); |
| // One word for the header, one for the timestamp, one for the counter, one for the argument |
| EXPECT_EQ(writer.bytes.size(), 32U); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = bytes[0]; |
| // Event type should be 0 |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0001'0000}); |
| // Timestamp should be correct |
| EXPECT_EQ(bytes[1], event_time); |
| // The counter should come after the arguments |
| EXPECT_EQ(bytes[3], counter_id); |
| } |
| |
| TEST(Serializer, DurationBeginEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteDurationBeginEventRecord(&writer, event_time, thread_ref, category_ref, |
| name_ref)); |
| // One word for the header, one for the time stamp |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(2).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| // Event type should be 2 |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0002'0000}); |
| EXPECT_EQ(words[1], event_time); |
| } |
| |
| TEST(Serializer, DurationEndEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteDurationEndEventRecord(&writer, event_time, thread_ref, category_ref, |
| name_ref)); |
| // One word for the header, one for the time stamp |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(2).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| // Event type should be 3 |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0003'0000}); |
| EXPECT_EQ(words[1], event_time); |
| } |
| |
| TEST(Serializer, DurationCompleteEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| uint64_t event_end_time = 0x1122'3344'5566'7788; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteDurationCompleteEventRecord(&writer, event_time, thread_ref, |
| category_ref, name_ref, event_end_time)); |
| // One word for the header, one for the start time, one for the end time |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| // Event type should be 4 |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0004'0000}); |
| EXPECT_EQ(words[1], event_time); |
| EXPECT_EQ(words[2], event_end_time); |
| } |
| |
| TEST(Serializer, AsyncBeginEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| uint64_t async_id = 0x1122'3344'5566'7788; |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteAsyncBeginEventRecord(&writer, event_time, thread_ref, category_ref, |
| name_ref, async_id)); |
| // One word for the header, one for the time stamp, one for the id |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0005'0000}); |
| EXPECT_EQ(words[1], event_time); |
| EXPECT_EQ(words[2], async_id); |
| } |
| |
| TEST(Serializer, AsyncInstantEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| uint64_t async_id = 0x1122'3344'5566'7788; |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteAsyncInstantEventRecord(&writer, event_time, thread_ref, category_ref, |
| name_ref, async_id)); |
| // One word for the header, one for the time stamp, one for the id |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0006'0000}); |
| EXPECT_EQ(words[1], event_time); |
| EXPECT_EQ(words[2], async_id); |
| } |
| |
| TEST(Serializer, AsyncEndEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| uint64_t async_id = 0x1122'3344'5566'7788; |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteAsyncEndEventRecord(&writer, event_time, thread_ref, category_ref, |
| name_ref, async_id)); |
| // One word for the header, one for the time stamp, one for the id |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0007'0000}); |
| EXPECT_EQ(words[1], event_time); |
| EXPECT_EQ(words[2], async_id); |
| } |
| |
| TEST(Serializer, FlowBeginEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| uint64_t flow_id = 0x1122'3344'5566'7788; |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteFlowBeginEventRecord(&writer, event_time, thread_ref, category_ref, |
| name_ref, flow_id)); |
| // One word for the header, one for the time stamp, one for the id |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0008'0000}); |
| EXPECT_EQ(words[1], event_time); |
| EXPECT_EQ(words[2], flow_id); |
| } |
| |
| TEST(Serializer, FlowInstantEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| uint64_t flow_id = 0x1122'3344'5566'7788; |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteFlowStepEventRecord(&writer, event_time, thread_ref, category_ref, |
| name_ref, flow_id)); |
| // One word for the header, one for the time stamp, one for the id |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'0009'0000}); |
| EXPECT_EQ(words[1], event_time); |
| EXPECT_EQ(words[2], flow_id); |
| } |
| |
| TEST(Serializer, FlowEndEventRecord) { |
| uint64_t event_time = 0x1234'5678'90AB'CDEF; |
| fxt::ThreadRef thread_ref(1); |
| fxt::StringRef category_ref(0x7777); |
| fxt::StringRef name_ref(0x1234); |
| uint64_t flow_id = 0x1122'3344'5566'7788; |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteFlowEndEventRecord(&writer, event_time, thread_ref, category_ref, |
| name_ref, flow_id)); |
| // One word for the header, one for the time stamp, one for the id |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| EXPECT_EQ(header & 0x0000'0000'000F'0000, uint64_t{0x0000'0000'000A'0000}); |
| EXPECT_EQ(words[1], event_time); |
| EXPECT_EQ(words[2], flow_id); |
| } |
| |
| TEST(Serializer, BlobRecord) { |
| fxt::StringRef blob_name(0x7777); |
| fxt::BlobType type = fxt::BlobType::kData; |
| const char* data = "This is some data that we are writing"; |
| size_t num_bytes = strlen(data); // 37 |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteBlobRecord(&writer, blob_name, type, |
| reinterpret_cast<const void*>(data), num_bytes)); |
| // One word for the header, five for the data |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(6).SizeInBytes()); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = bytes[0]; |
| // Record type is 5 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'0005}); |
| // Size |
| EXPECT_EQ(header & 0x0000'0000'0000'FFF0, uint64_t{0x0000'0000'0000'0060}); |
| // Block name ref |
| EXPECT_EQ(header & 0x0000'0000'FFFF'0000, uint64_t{0x0000'0000'7777'0000}); |
| // Blob size |
| EXPECT_EQ(header & 0x0000'7FFF'0000'0000, uint64_t{0x0000'0025'0000'0000}); |
| // Type |
| EXPECT_EQ(header & 0x00FF'0000'0000'0000, uint64_t{0x0001'0000'0000'0000}); |
| EXPECT_EQ(std::memcmp(bytes + 1, "This is ", 8), 0); |
| EXPECT_EQ(std::memcmp(bytes + 2, "some dat", 8), 0); |
| EXPECT_EQ(std::memcmp(bytes + 3, "a that w", 8), 0); |
| EXPECT_EQ(std::memcmp(bytes + 4, "e are wr", 8), 0); |
| EXPECT_EQ(std::memcmp(bytes + 5, "iting\0\0\0", 8), 0); |
| } |
| |
| TEST(Serializer, UserspaceObjectRecord) { |
| fxt::StringRef name(0x7777); |
| fxt::StringRef arg_name(0x1234); |
| fxt::ThreadRef thread(0xAA); |
| uintptr_t ptr = 0xDEADBEEF; |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteUserspaceObjectRecord(&writer, ptr, thread, name, |
| fxt::Argument(arg_name, true))); |
| // 1 word for the header, 1 for the pointer, 1 for the argument |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = bytes[0]; |
| // Record type is 6 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'0006}); |
| // Size |
| EXPECT_EQ(header & 0x0000'0000'0000'FFF0, uint64_t{0x0000'0000'0000'0030}); |
| // Threadref |
| EXPECT_EQ(header & 0x0000'0000'00FF'0000, uint64_t{0x0000'0000'00AA'0000}); |
| // Name Ref |
| EXPECT_EQ(header & 0x0000'00FF'FF00'0000, uint64_t{0x0000'0077'7700'0000}); |
| EXPECT_EQ(bytes[1], ptr); |
| // Argument (true) |
| EXPECT_EQ(bytes[2], uint64_t{0x0000'0001'1234'0019}); |
| } |
| |
| TEST(Serializer, KernelObjectRecord) { |
| fxt::StringRef name(0x7777); |
| fxt::StringRef arg_name(0x4321); |
| zx_koid_t koid = 0xDEADBEEF; |
| zx_obj_type_t obj_type = ZX_OBJ_TYPE_CHANNEL; |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteKernelObjectRecord(&writer, koid, obj_type, name, |
| fxt::Argument(arg_name, false))); |
| // 1 word for the header, 1 for the pointer, 1 for the argument |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(3).SizeInBytes()); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = bytes[0]; |
| // Record type is 7 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'0007}); |
| // Size |
| EXPECT_EQ(header & 0x0000'0000'0000'FFF0, uint64_t{0x0000'0000'0000'0030}); |
| // Obj type |
| EXPECT_EQ(header & 0x0000'0000'00FF'0000, uint64_t{0x0000'0000'0004'0000}); |
| // Name Ref |
| EXPECT_EQ(header & 0x0000'00FF'FF00'0000, uint64_t{0x0000'0077'7700'0000}); |
| EXPECT_EQ(bytes[1], koid); |
| // Argument (true) |
| EXPECT_EQ(bytes[2], uint64_t{0x0000'0000'4321'0019}); |
| } |
| |
| TEST(Serializer, ContextSwitchRecord) { |
| uint64_t event_time = 0xAABB'CCDD'EEFF'0011; |
| uint8_t cpu_number = 0xBB; |
| zx_thread_state_t outgoing_state = ZX_THREAD_STATE_SUSPENDED; |
| fxt::ThreadRef outgoing_thread(0x1); |
| fxt::ThreadRef incoming_thread(0x2); |
| uint8_t outgoing_thread_pri = 3; |
| uint8_t incoming_thread_pri = 4; |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteContextSwitchRecord(&writer, event_time, cpu_number, outgoing_state, |
| outgoing_thread, incoming_thread, |
| outgoing_thread_pri, incoming_thread_pri)); |
| // 1 word for the header, 1 for the timestamp |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(2).SizeInBytes()); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = bytes[0]; |
| // Record type is 8 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'0008}); |
| // Size |
| EXPECT_EQ(header & 0x0000'0000'0000'FFF0, uint64_t{0x0000'0000'0000'0020}); |
| // CPU number |
| EXPECT_EQ(header & 0x0000'0000'00FF'0000, uint64_t{0x0000'0000'00BB'0000}); |
| // State |
| EXPECT_EQ(header & 0x0000'0000'0F00'0000, uint64_t{0x0000'0000'0200'0000}); |
| // out ref |
| EXPECT_EQ(header & 0x0000'000F'F000'0000, uint64_t{0x0000'0000'1000'0000}); |
| // in ref |
| EXPECT_EQ(header & 0x0000'0FF0'0000'0000, uint64_t{0x0000'0020'0000'0000}); |
| // out pri |
| EXPECT_EQ(header & 0x000F'F000'0000'0000, uint64_t{0x0000'3000'0000'0000}); |
| // in pri |
| EXPECT_EQ(header & 0x0FF0'0000'0000'0000, uint64_t{0x0040'0000'0000'0000}); |
| EXPECT_EQ(bytes[1], event_time); |
| } |
| |
| TEST(Serializer, LogRecord) { |
| uint64_t event_time = 0xAABB'CCDD'EEFF'0011; |
| fxt::ThreadRef log_thread(0x1); |
| const char* message = "This is a log message"; |
| size_t message_len = strlen(message); // 21 |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteLogRecord(&writer, event_time, log_thread, message, message_len)); |
| // 1 word for the header, 1 for the timestamp, 3 words for the message |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(5).SizeInBytes()); |
| uint64_t* bytes = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = bytes[0]; |
| // Record type is 9 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'0009}); |
| // Size |
| EXPECT_EQ(header & 0x0000'0000'0000'FFF0, uint64_t{0x0000'0000'0000'0050}); |
| // message length |
| EXPECT_EQ(header & 0x0000'0000'8FFF'0000, uint64_t{0x0000'0000'0015'0000}); |
| // thread_ref |
| EXPECT_EQ(header & 0x0000'00FF'0000'0000, uint64_t{0x0000'0001'0000'0000}); |
| EXPECT_EQ(bytes[1], event_time); |
| EXPECT_EQ(std::memcmp(bytes + 2, "This is ", 8), 0); |
| EXPECT_EQ(std::memcmp(bytes + 3, "a log me", 8), 0); |
| EXPECT_EQ(std::memcmp(bytes + 4, "ssage\0\0\0", 8), 0); |
| } |
| |
| TEST(Serializer, LargeBlobWithMetadataRecord) { |
| uint64_t event_time = 0xAABB'CCDD'EEFF'0011; |
| fxt::StringRef category_ref(0x7AAA); |
| fxt::StringRef name_ref(0x7BBB); |
| fxt::ThreadRef thread_ref(0xCC); |
| fxt::StringRef arg_name(0x2345); |
| const char* data = "Some data to write into the buffer"; |
| size_t blob_size_bytes = strlen(data); // 34 |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteLargeBlobRecordWithMetadata( |
| &writer, event_time, category_ref, name_ref, thread_ref, data, |
| blob_size_bytes, fxt::Argument(arg_name, true))); |
| |
| // 1 word for the large header, 1 for the blob header, 1 for timestamp, 1 for |
| // the argument header, 1 for blob size, 5 for payload. |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(10).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| // Record type is 15 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'000F}); |
| // Size |
| EXPECT_EQ(header & 0x0000'000F'FFFF'FFF0, uint64_t{0x0000'0000'0000'00A0}); |
| // large record type (0) |
| EXPECT_EQ(header & 0x0000'00F0'0000'0000, uint64_t{0x0000'0000'0000'0000}); |
| // blob format type (0) |
| EXPECT_EQ(header & 0x0000'0F00'0000'0000, uint64_t{0x0000'0000'0000'0000}); |
| uint64_t blob_header = words[1]; |
| // Category Ref |
| EXPECT_EQ(blob_header & 0x0000'0000'0000'FFFF, uint64_t{0x0000'0000'0000'7AAA}); |
| // Name Ref |
| EXPECT_EQ(blob_header & 0x0000'0000'FFFF'0000, uint64_t{0x0000'0000'7BBB'0000}); |
| // thread |
| EXPECT_EQ(blob_header & 0x0000'0FF0'0000'0000, uint64_t{0x0000'0CC0'0000'0000}); |
| |
| EXPECT_EQ(words[2], event_time); |
| // Argument |
| EXPECT_EQ(words[3], uint64_t{0x0000'0001'2345'0019}); |
| EXPECT_EQ(words[4], blob_size_bytes); |
| EXPECT_EQ(std::memcmp(words + 5, "Some dat", 8), 0); |
| EXPECT_EQ(std::memcmp(words + 6, "a to wri", 8), 0); |
| EXPECT_EQ(std::memcmp(words + 7, "te into ", 8), 0); |
| EXPECT_EQ(std::memcmp(words + 8, "the buff", 8), 0); |
| EXPECT_EQ(std::memcmp(words + 9, "er\0\0\0\0\0\0", 8), 0); |
| } |
| |
| TEST(Serializer, LargeBlobWithNoMetadataRecord) { |
| fxt::StringRef category_ref(0x7AAA); |
| fxt::StringRef name_ref(0x7BBB); |
| const char* data = "Some data to write into the buffer"; |
| size_t blob_size_bytes = strlen(data); // 34 |
| |
| FakeWriter writer; |
| EXPECT_EQ(ZX_OK, fxt::WriteLargeBlobRecordWithNoMetadata(&writer, category_ref, name_ref, data, |
| blob_size_bytes)); |
| // 1 word for the large header, 1 for the blob header, 1 for |
| // blob size, 5 for payload. |
| EXPECT_EQ(writer.bytes.size(), fxt::WordSize(8).SizeInBytes()); |
| uint64_t* words = reinterpret_cast<uint64_t*>(writer.bytes.data()); |
| uint64_t header = words[0]; |
| // Record type is 15 |
| EXPECT_EQ(header & 0x0000'0000'0000'000F, uint64_t{0x0000'0000'0000'000F}); |
| // Size |
| EXPECT_EQ(header & 0x0000'000F'FFFF'FFF0, uint64_t{0x0000'0000'0000'0090}); |
| // large record type (0) |
| EXPECT_EQ(header & 0x0000'00F0'0000'0000, uint64_t{0x0000'0000'0000'0000}); |
| // blob format type (0) |
| EXPECT_EQ(header & 0x0000'0F00'0000'0000, uint64_t{0x0000'0000'0000'0000}); |
| uint64_t blob_header = words[1]; |
| // Category Ref |
| EXPECT_EQ(blob_header & 0x0000'0000'0000'FFFF, uint64_t{0x0000'0000'0000'7AAA}); |
| // Name Ref |
| EXPECT_EQ(blob_header & 0x0000'0000'FFFF'0000, uint64_t{0x0000'0000'7BBB'0000}); |
| |
| EXPECT_EQ(words[2], blob_size_bytes); |
| EXPECT_EQ(std::memcmp(words + 3, "Some dat", 8), 0); |
| EXPECT_EQ(std::memcmp(words + 4, "a to wri", 8), 0); |
| EXPECT_EQ(std::memcmp(words + 5, "te into ", 8), 0); |
| EXPECT_EQ(std::memcmp(words + 6, "the buff", 8), 0); |
| EXPECT_EQ(std::memcmp(words + 7, "er\0\0\0\0\0\0", 8), 0); |
| } |
| |
| } // namespace |