| 1 | #include "memprof/memprof_rawprofile.h" |
| 2 | |
| 3 | #include <cstdint> |
| 4 | #include <memory> |
| 5 | |
| 6 | #include "profile/MemProfData.inc" |
| 7 | #include "sanitizer_common/sanitizer_array_ref.h" |
| 8 | #include "sanitizer_common/sanitizer_common.h" |
| 9 | #include "sanitizer_common/sanitizer_procmaps.h" |
| 10 | #include "sanitizer_common/sanitizer_stackdepot.h" |
| 11 | #include "sanitizer_common/sanitizer_stacktrace.h" |
| 12 | #include "gmock/gmock.h" |
| 13 | #include "gtest/gtest.h" |
| 14 | |
| 15 | namespace { |
| 16 | |
| 17 | using ::__memprof::MIBMapTy; |
| 18 | using ::__memprof::SerializeToRawProfile; |
| 19 | using ::__sanitizer::StackDepotPut; |
| 20 | using ::__sanitizer::StackTrace; |
| 21 | using ::llvm::memprof::MemInfoBlock; |
| 22 | |
| 23 | uint64_t PopulateFakeMap(const MemInfoBlock &FakeMIB, uintptr_t StackPCBegin, |
| 24 | MIBMapTy &FakeMap) { |
| 25 | constexpr int kSize = 5; |
| 26 | uintptr_t array[kSize]; |
| 27 | for (int i = 0; i < kSize; i++) { |
| 28 | array[i] = StackPCBegin + i; |
| 29 | } |
| 30 | StackTrace St(array, kSize); |
| 31 | uint32_t Id = StackDepotPut(stack: St); |
| 32 | |
| 33 | InsertOrMerge(Id, Block: FakeMIB, Map&: FakeMap); |
| 34 | return Id; |
| 35 | } |
| 36 | |
| 37 | template <class T = uint64_t> T Read(char *&Buffer) { |
| 38 | static_assert(std::is_pod<T>::value, "Must be a POD type." ); |
| 39 | assert(reinterpret_cast<size_t>(Buffer) % sizeof(T) == 0 && |
| 40 | "Unaligned read!" ); |
| 41 | T t = *reinterpret_cast<T *>(Buffer); |
| 42 | Buffer += sizeof(T); |
| 43 | return t; |
| 44 | } |
| 45 | |
| 46 | TEST(MemProf, Basic) { |
| 47 | __sanitizer::LoadedModule FakeModule; |
| 48 | FakeModule.addAddressRange(/*begin=*/beg: 0x10, /*end=*/0x20, /*executable=*/true, |
| 49 | /*writable=*/false, /*name=*/"" ); |
| 50 | const char uuid[MEMPROF_BUILDID_MAX_SIZE] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE}; |
| 51 | FakeModule.setUuid(uuid, MEMPROF_BUILDID_MAX_SIZE); |
| 52 | __sanitizer::ArrayRef<__sanitizer::LoadedModule> Modules(&FakeModule, |
| 53 | (&FakeModule) + 1); |
| 54 | |
| 55 | MIBMapTy FakeMap; |
| 56 | MemInfoBlock FakeMIB; |
| 57 | // Since we want to override the constructor set vals to make it easier to |
| 58 | // test. |
| 59 | memset(s: &FakeMIB, c: 0, n: sizeof(MemInfoBlock)); |
| 60 | FakeMIB.AllocCount = 0x1; |
| 61 | FakeMIB.TotalAccessCount = 0x2; |
| 62 | |
| 63 | uint64_t FakeIds[2]; |
| 64 | FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap); |
| 65 | FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap); |
| 66 | |
| 67 | char *Ptr = nullptr; |
| 68 | uint64_t NumBytes = SerializeToRawProfile(BlockCache&: FakeMap, Modules, Buffer&: Ptr); |
| 69 | const char *Buffer = Ptr; |
| 70 | |
| 71 | ASSERT_GT(NumBytes, 0ULL); |
| 72 | ASSERT_TRUE(Ptr); |
| 73 | |
| 74 | // Check the header. |
| 75 | EXPECT_THAT(Read(Buffer&: Ptr), MEMPROF_RAW_MAGIC_64); |
| 76 | EXPECT_THAT(Read(Buffer&: Ptr), MEMPROF_RAW_VERSION); |
| 77 | const uint64_t TotalSize = Read(Buffer&: Ptr); |
| 78 | const uint64_t SegmentOffset = Read(Buffer&: Ptr); |
| 79 | const uint64_t MIBOffset = Read(Buffer&: Ptr); |
| 80 | const uint64_t StackOffset = Read(Buffer&: Ptr); |
| 81 | |
| 82 | // ============= Check sizes and padding. |
| 83 | EXPECT_EQ(TotalSize, NumBytes); |
| 84 | EXPECT_EQ(TotalSize % 8, 0ULL); |
| 85 | |
| 86 | // Should be equal to the size of the raw profile header. |
| 87 | EXPECT_EQ(SegmentOffset, 48ULL); |
| 88 | |
| 89 | // We expect only 1 segment entry, 8b for the count and 64b for SegmentEntry |
| 90 | // in memprof_rawprofile.cpp. |
| 91 | EXPECT_EQ(MIBOffset - SegmentOffset, 72ULL); |
| 92 | |
| 93 | EXPECT_EQ(MIBOffset, 120ULL); |
| 94 | // We expect 2 mib entry, 8b for the count and sizeof(uint64_t) + |
| 95 | // sizeof(MemInfoBlock) contains stack id + MeminfoBlock. |
| 96 | EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock))); |
| 97 | |
| 98 | EXPECT_EQ(StackOffset, 432ULL); |
| 99 | // We expect 2 stack entries, with 5 frames - 8b for total count, |
| 100 | // 2 * (8b for id, 8b for frame count and 5*8b for fake frames). |
| 101 | // Since this is the last section, there may be additional padding at the end |
| 102 | // to make the total profile size 8b aligned. |
| 103 | EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8)); |
| 104 | |
| 105 | // ============= Check contents. |
| 106 | unsigned char ExpectedSegmentBytes[72] = { |
| 107 | 0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries |
| 108 | 0x10, 0, 0, 0, 0, 0, 0, 0, // Start |
| 109 | 0x20, 0, 0, 0, 0, 0, 0, 0, // End |
| 110 | 0x0, 0, 0, 0, 0, 0, 0, 0, // Offset |
| 111 | 0x20, 0, 0, 0, 0, 0, 0, 0, // UuidSize |
| 112 | 0xC, 0x0, 0xF, 0xF, 0xE, 0xE // Uuid |
| 113 | }; |
| 114 | EXPECT_EQ(memcmp(s1: Buffer + SegmentOffset, s2: ExpectedSegmentBytes, n: 72), 0); |
| 115 | |
| 116 | // Check that the number of entries is 2. |
| 117 | EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset), 2ULL); |
| 118 | // Check that stack id is set. |
| 119 | EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset + 8), |
| 120 | FakeIds[0]); |
| 121 | |
| 122 | // Only check a few fields of the first MemInfoBlock. |
| 123 | unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = { |
| 124 | 0x01, 0, 0, 0, // Alloc count |
| 125 | 0x02, 0, 0, 0, // Total access count |
| 126 | }; |
| 127 | // Compare contents of 1st MIB after skipping count and stack id. |
| 128 | EXPECT_EQ( |
| 129 | memcmp(s1: Buffer + MIBOffset + 16, s2: ExpectedMIBBytes, n: sizeof(MemInfoBlock)), |
| 130 | 0); |
| 131 | // Compare contents of 2nd MIB after skipping count and stack id for the first |
| 132 | // and only the id for the second. |
| 133 | EXPECT_EQ(memcmp(s1: Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8, |
| 134 | s2: ExpectedMIBBytes, n: sizeof(MemInfoBlock)), |
| 135 | 0); |
| 136 | |
| 137 | // Check that the number of entries is 2. |
| 138 | EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset), 2ULL); |
| 139 | // Check that the 1st stack id is set. |
| 140 | EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8), |
| 141 | FakeIds[0]); |
| 142 | // Contents are num pcs, value of each pc - 1. |
| 143 | unsigned char ExpectedStackBytes[2][6 * 8] = { |
| 144 | { |
| 145 | 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs |
| 146 | 0x1, 0, 0, 0, 0, 0, 0, 0, // PC ... |
| 147 | 0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, |
| 148 | 0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0, |
| 149 | }, |
| 150 | { |
| 151 | 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs |
| 152 | 0x2, 0, 0, 0, 0, 0, 0, 0, // PC ... |
| 153 | 0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, |
| 154 | 0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0, |
| 155 | }, |
| 156 | }; |
| 157 | EXPECT_EQ(memcmp(s1: Buffer + StackOffset + 16, s2: ExpectedStackBytes[0], |
| 158 | n: sizeof(ExpectedStackBytes[0])), |
| 159 | 0); |
| 160 | |
| 161 | // Check that the 2nd stack id is set. |
| 162 | EXPECT_EQ( |
| 163 | *reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8 + 6 * 8 + 8), |
| 164 | FakeIds[1]); |
| 165 | |
| 166 | EXPECT_EQ(memcmp(s1: Buffer + StackOffset + 16 + 6 * 8 + 8, s2: ExpectedStackBytes[1], |
| 167 | n: sizeof(ExpectedStackBytes[1])), |
| 168 | 0); |
| 169 | } |
| 170 | } // namespace |
| 171 | |