| #include "memprof/memprof_rawprofile.h" |
| |
| #include <cstdint> |
| #include <memory> |
| |
| #include "profile/MemProfData.inc" |
| #include "sanitizer_common/sanitizer_array_ref.h" |
| #include "sanitizer_common/sanitizer_common.h" |
| #include "sanitizer_common/sanitizer_procmaps.h" |
| #include "sanitizer_common/sanitizer_stackdepot.h" |
| #include "sanitizer_common/sanitizer_stacktrace.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| namespace { |
| |
| using ::__memprof::MIBMapTy; |
| using ::__memprof::SerializeToRawProfile; |
| using ::__sanitizer::StackDepotPut; |
| using ::__sanitizer::StackTrace; |
| using ::llvm::memprof::MemInfoBlock; |
| |
| uint64_t PopulateFakeMap(const MemInfoBlock &FakeMIB, uintptr_t StackPCBegin, |
| MIBMapTy &FakeMap) { |
| constexpr int kSize = 5; |
| uintptr_t array[kSize]; |
| for (int i = 0; i < kSize; i++) { |
| array[i] = StackPCBegin + i; |
| } |
| StackTrace St(array, kSize); |
| uint32_t Id = StackDepotPut(St); |
| |
| InsertOrMerge(Id, FakeMIB, FakeMap); |
| return Id; |
| } |
| |
| template <class T = uint64_t> T Read(char *&Buffer) { |
| static_assert(std::is_pod<T>::value, "Must be a POD type."); |
| assert(reinterpret_cast<size_t>(Buffer) % sizeof(T) == 0 && |
| "Unaligned read!"); |
| T t = *reinterpret_cast<T *>(Buffer); |
| Buffer += sizeof(T); |
| return t; |
| } |
| |
| TEST(MemProf, Basic) { |
| __sanitizer::LoadedModule FakeModule; |
| FakeModule.addAddressRange(/*begin=*/0x10, /*end=*/0x20, /*executable=*/true, |
| /*writable=*/false, /*name=*/""); |
| const char uuid[MEMPROF_BUILDID_MAX_SIZE] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE}; |
| FakeModule.setUuid(uuid, MEMPROF_BUILDID_MAX_SIZE); |
| __sanitizer::ArrayRef<__sanitizer::LoadedModule> Modules(&FakeModule, |
| (&FakeModule) + 1); |
| |
| MIBMapTy FakeMap; |
| MemInfoBlock FakeMIB; |
| // Since we want to override the constructor set vals to make it easier to |
| // test. |
| memset(&FakeMIB, 0, sizeof(MemInfoBlock)); |
| FakeMIB.AllocCount = 0x1; |
| FakeMIB.TotalAccessCount = 0x2; |
| |
| uint64_t FakeIds[2]; |
| FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap); |
| FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap); |
| |
| char *Ptr = nullptr; |
| uint64_t NumBytes = SerializeToRawProfile(FakeMap, Modules, Ptr); |
| const char *Buffer = Ptr; |
| |
| ASSERT_GT(NumBytes, 0ULL); |
| ASSERT_TRUE(Ptr); |
| |
| // Check the header. |
| EXPECT_THAT(Read(Ptr), MEMPROF_RAW_MAGIC_64); |
| EXPECT_THAT(Read(Ptr), MEMPROF_RAW_VERSION); |
| const uint64_t TotalSize = Read(Ptr); |
| const uint64_t SegmentOffset = Read(Ptr); |
| const uint64_t MIBOffset = Read(Ptr); |
| const uint64_t StackOffset = Read(Ptr); |
| |
| // ============= Check sizes and padding. |
| EXPECT_EQ(TotalSize, NumBytes); |
| EXPECT_EQ(TotalSize % 8, 0ULL); |
| |
| // Should be equal to the size of the raw profile header. |
| EXPECT_EQ(SegmentOffset, 48ULL); |
| |
| // We expect only 1 segment entry, 8b for the count and 64b for SegmentEntry |
| // in memprof_rawprofile.cpp. |
| EXPECT_EQ(MIBOffset - SegmentOffset, 72ULL); |
| |
| EXPECT_EQ(MIBOffset, 120ULL); |
| // We expect 2 mib entry, 8b for the count and sizeof(uint64_t) + |
| // sizeof(MemInfoBlock) contains stack id + MeminfoBlock. |
| EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock))); |
| |
| EXPECT_EQ(StackOffset, 432ULL); |
| // We expect 2 stack entries, with 5 frames - 8b for total count, |
| // 2 * (8b for id, 8b for frame count and 5*8b for fake frames). |
| // Since this is the last section, there may be additional padding at the end |
| // to make the total profile size 8b aligned. |
| EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8)); |
| |
| // ============= Check contents. |
| unsigned char ExpectedSegmentBytes[72] = { |
| 0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries |
| 0x10, 0, 0, 0, 0, 0, 0, 0, // Start |
| 0x20, 0, 0, 0, 0, 0, 0, 0, // End |
| 0x0, 0, 0, 0, 0, 0, 0, 0, // Offset |
| 0x20, 0, 0, 0, 0, 0, 0, 0, // UuidSize |
| 0xC, 0x0, 0xF, 0xF, 0xE, 0xE // Uuid |
| }; |
| EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 72), 0); |
| |
| // Check that the number of entries is 2. |
| EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset), 2ULL); |
| // Check that stack id is set. |
| EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset + 8), |
| FakeIds[0]); |
| |
| // Only check a few fields of the first MemInfoBlock. |
| unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = { |
| 0x01, 0, 0, 0, // Alloc count |
| 0x02, 0, 0, 0, // Total access count |
| }; |
| // Compare contents of 1st MIB after skipping count and stack id. |
| EXPECT_EQ( |
| memcmp(Buffer + MIBOffset + 16, ExpectedMIBBytes, sizeof(MemInfoBlock)), |
| 0); |
| // Compare contents of 2nd MIB after skipping count and stack id for the first |
| // and only the id for the second. |
| EXPECT_EQ(memcmp(Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8, |
| ExpectedMIBBytes, sizeof(MemInfoBlock)), |
| 0); |
| |
| // Check that the number of entries is 2. |
| EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset), 2ULL); |
| // Check that the 1st stack id is set. |
| EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8), |
| FakeIds[0]); |
| // Contents are num pcs, value of each pc - 1. |
| unsigned char ExpectedStackBytes[2][6 * 8] = { |
| { |
| 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs |
| 0x1, 0, 0, 0, 0, 0, 0, 0, // PC ... |
| 0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, |
| 0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0, |
| }, |
| { |
| 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs |
| 0x2, 0, 0, 0, 0, 0, 0, 0, // PC ... |
| 0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, |
| 0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0, |
| }, |
| }; |
| EXPECT_EQ(memcmp(Buffer + StackOffset + 16, ExpectedStackBytes[0], |
| sizeof(ExpectedStackBytes[0])), |
| 0); |
| |
| // Check that the 2nd stack id is set. |
| EXPECT_EQ( |
| *reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8 + 6 * 8 + 8), |
| FakeIds[1]); |
| |
| EXPECT_EQ(memcmp(Buffer + StackOffset + 16 + 6 * 8 + 8, ExpectedStackBytes[1], |
| sizeof(ExpectedStackBytes[1])), |
| 0); |
| } |
| } // namespace |