1 #include "memprof/memprof_rawprofile.h" 2 3 #include <cstdint> 4 #include <memory> 5 6 #include "memprof/memprof_meminfoblock.h" 7 #include "profile/MemProfData.inc" 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::MemInfoBlock; 18 using ::__memprof::MIBMapTy; 19 using ::__memprof::SerializeToRawProfile; 20 using ::__sanitizer::MemoryMappedSegment; 21 using ::__sanitizer::MemoryMappingLayoutBase; 22 using ::__sanitizer::StackDepotPut; 23 using ::__sanitizer::StackTrace; 24 using ::testing::_; 25 using ::testing::Action; 26 using ::testing::DoAll; 27 using ::testing::Return; 28 using ::testing::SetArgPointee; 29 30 class MockMemoryMappingLayout final : public MemoryMappingLayoutBase { 31 public: 32 MOCK_METHOD(bool, Next, (MemoryMappedSegment *), (override)); 33 MOCK_METHOD(void, Reset, (), (override)); 34 }; 35 36 u64 PopulateFakeMap(const MemInfoBlock &FakeMIB, uptr StackPCBegin, 37 MIBMapTy &FakeMap) { 38 constexpr int kSize = 5; 39 uptr array[kSize]; 40 for (int i = 0; i < kSize; i++) { 41 array[i] = StackPCBegin + i; 42 } 43 StackTrace St(array, kSize); 44 u32 Id = StackDepotPut(St); 45 46 InsertOrMerge(Id, FakeMIB, FakeMap); 47 return Id; 48 } 49 50 template <class T = u64> T Read(char *&Buffer) { 51 static_assert(std::is_pod<T>::value, "Must be a POD type."); 52 assert(reinterpret_cast<size_t>(Buffer) % sizeof(T) == 0 && 53 "Unaligned read!"); 54 T t = *reinterpret_cast<T *>(Buffer); 55 Buffer += sizeof(T); 56 return t; 57 } 58 59 TEST(MemProf, Basic) { 60 MockMemoryMappingLayout Layout; 61 MemoryMappedSegment FakeSegment; 62 memset(&FakeSegment, 0, sizeof(FakeSegment)); 63 FakeSegment.start = 0x10; 64 FakeSegment.end = 0x20; 65 FakeSegment.offset = 0x10; 66 uint8_t uuid[__sanitizer::kModuleUUIDSize] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE}; 67 memcpy(FakeSegment.uuid, uuid, __sanitizer::kModuleUUIDSize); 68 FakeSegment.protection = 69 __sanitizer::kProtectionExecute | __sanitizer::kProtectionRead; 70 71 const Action<bool(MemoryMappedSegment *)> SetSegment = 72 DoAll(SetArgPointee<0>(FakeSegment), Return(true)); 73 EXPECT_CALL(Layout, Next(_)) 74 .WillOnce(SetSegment) 75 .WillOnce(Return(false)) 76 .WillOnce(SetSegment) 77 .WillRepeatedly(Return(false)); 78 79 EXPECT_CALL(Layout, Reset).Times(2); 80 81 MIBMapTy FakeMap; 82 MemInfoBlock FakeMIB; 83 // Since we want to override the constructor set vals to make it easier to 84 // test. 85 memset(&FakeMIB, 0, sizeof(MemInfoBlock)); 86 FakeMIB.alloc_count = 0x1; 87 FakeMIB.total_access_count = 0x2; 88 89 u64 FakeIds[2]; 90 FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap); 91 FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap); 92 93 char *Ptr = nullptr; 94 u64 NumBytes = SerializeToRawProfile(FakeMap, Layout, Ptr); 95 const char *Buffer = Ptr; 96 97 ASSERT_GT(NumBytes, 0ULL); 98 ASSERT_TRUE(Ptr); 99 100 // Check the header. 101 EXPECT_THAT(Read(Ptr), MEMPROF_RAW_MAGIC_64); 102 EXPECT_THAT(Read(Ptr), MEMPROF_RAW_VERSION); 103 const u64 TotalSize = Read(Ptr); 104 const u64 SegmentOffset = Read(Ptr); 105 const u64 MIBOffset = Read(Ptr); 106 const u64 StackOffset = Read(Ptr); 107 108 // ============= Check sizes and padding. 109 EXPECT_EQ(TotalSize, NumBytes); 110 EXPECT_EQ(TotalSize % 8, 0ULL); 111 112 // Should be equal to the size of the raw profile header. 113 EXPECT_EQ(SegmentOffset, 48ULL); 114 115 // We expect only 1 segment entry, 8b for the count and 56b for SegmentEntry 116 // in memprof_rawprofile.cpp. 117 EXPECT_EQ(MIBOffset - SegmentOffset, 64ULL); 118 119 EXPECT_EQ(MIBOffset, 112ULL); 120 // We expect 2 mib entry, 8b for the count and sizeof(u64) + 121 // sizeof(MemInfoBlock) contains stack id + MeminfoBlock. 122 EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock))); 123 124 EXPECT_EQ(StackOffset, 336ULL); 125 // We expect 2 stack entries, with 5 frames - 8b for total count, 126 // 2 * (8b for id, 8b for frame count and 5*8b for fake frames). 127 // Since this is the last section, there may be additional padding at the end 128 // to make the total profile size 8b aligned. 129 EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8)); 130 131 // ============= Check contents. 132 unsigned char ExpectedSegmentBytes[64] = { 133 0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries 134 0x10, 0, 0, 0, 0, 0, 0, 0, // Start 135 0x20, 0, 0, 0, 0, 0, 0, 0, // End 136 0x10, 0, 0, 0, 0, 0, 0, 0, // Offset 137 0x0C, 0x0, 0xF, 0xF, 0xE, 0xE, // Uuid 138 }; 139 EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 64), 0); 140 141 // Check that the number of entries is 2. 142 EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset), 2ULL); 143 // Check that stack id is set. 144 EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset + 8), FakeIds[0]); 145 146 // Only check a few fields of the first MemInfoBlock. 147 unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = { 148 0x01, 0, 0, 0, // Alloc count 149 0x02, 0, 0, 0, // Total access count 150 }; 151 // Compare contents of 1st MIB after skipping count and stack id. 152 EXPECT_EQ( 153 memcmp(Buffer + MIBOffset + 16, ExpectedMIBBytes, sizeof(MemInfoBlock)), 154 0); 155 // Compare contents of 2nd MIB after skipping count and stack id for the first 156 // and only the id for the second. 157 EXPECT_EQ(memcmp(Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8, 158 ExpectedMIBBytes, sizeof(MemInfoBlock)), 159 0); 160 161 // Check that the number of entries is 2. 162 EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset), 2ULL); 163 // Check that the 1st stack id is set. 164 EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset + 8), 165 FakeIds[0]); 166 // Contents are num pcs, value of each pc - 1. 167 unsigned char ExpectedStackBytes[2][6 * 8] = { 168 { 169 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs 170 0x1, 0, 0, 0, 0, 0, 0, 0, // PC ... 171 0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, 172 0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0, 173 }, 174 { 175 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs 176 0x2, 0, 0, 0, 0, 0, 0, 0, // PC ... 177 0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 178 0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0, 179 }, 180 }; 181 EXPECT_EQ(memcmp(Buffer + StackOffset + 16, ExpectedStackBytes[0], 182 sizeof(ExpectedStackBytes[0])), 183 0); 184 185 // Check that the 2nd stack id is set. 186 EXPECT_EQ( 187 *reinterpret_cast<const u64 *>(Buffer + StackOffset + 8 + 6 * 8 + 8), 188 FakeIds[1]); 189 190 EXPECT_EQ(memcmp(Buffer + StackOffset + 16 + 6 * 8 + 8, ExpectedStackBytes[1], 191 sizeof(ExpectedStackBytes[1])), 192 0); 193 } 194 195 } // namespace 196