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 T t = *reinterpret_cast<T *>(Buffer); 53 Buffer += sizeof(T); 54 return t; 55 } 56 57 TEST(MemProf, Basic) { 58 MockMemoryMappingLayout Layout; 59 MemoryMappedSegment FakeSegment; 60 memset(&FakeSegment, 0, sizeof(FakeSegment)); 61 FakeSegment.start = 0x10; 62 FakeSegment.end = 0x20; 63 FakeSegment.offset = 0x10; 64 uint8_t uuid[__sanitizer::kModuleUUIDSize] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE}; 65 memcpy(FakeSegment.uuid, uuid, __sanitizer::kModuleUUIDSize); 66 FakeSegment.protection = 67 __sanitizer::kProtectionExecute | __sanitizer::kProtectionRead; 68 69 const Action<bool(MemoryMappedSegment *)> SetSegment = 70 DoAll(SetArgPointee<0>(FakeSegment), Return(true)); 71 EXPECT_CALL(Layout, Next(_)) 72 .WillOnce(SetSegment) 73 .WillOnce(Return(false)) 74 .WillOnce(SetSegment) 75 .WillRepeatedly(Return(false)); 76 77 EXPECT_CALL(Layout, Reset).Times(2); 78 79 MIBMapTy FakeMap; 80 MemInfoBlock FakeMIB; 81 // Since we want to override the constructor set vals to make it easier to 82 // test. 83 memset(&FakeMIB, 0, sizeof(MemInfoBlock)); 84 FakeMIB.alloc_count = 0x1; 85 FakeMIB.total_access_count = 0x2; 86 87 u64 FakeIds[2]; 88 FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap); 89 FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap); 90 91 char *Ptr = nullptr; 92 u64 NumBytes = SerializeToRawProfile(FakeMap, Layout, Ptr); 93 const char *Buffer = Ptr; 94 95 ASSERT_GT(NumBytes, 0ULL); 96 ASSERT_TRUE(Ptr); 97 98 // Check the header. 99 EXPECT_THAT(Read(Ptr), MEMPROF_RAW_MAGIC_64); 100 EXPECT_THAT(Read(Ptr), MEMPROF_RAW_VERSION); 101 const u64 TotalSize = Read(Ptr); 102 const u64 SegmentOffset = Read(Ptr); 103 const u64 MIBOffset = Read(Ptr); 104 const u64 StackOffset = Read(Ptr); 105 106 // ============= Check sizes. 107 EXPECT_EQ(TotalSize, NumBytes); 108 109 // Should be equal to the size of the raw profile header. 110 EXPECT_EQ(SegmentOffset, 48ULL); 111 112 // We expect only 1 segment entry, 8b for the count and 56b for SegmentEntry 113 // in memprof_rawprofile.cpp. 114 EXPECT_EQ(MIBOffset - SegmentOffset, 64ULL); 115 116 EXPECT_EQ(MIBOffset, 112ULL); 117 // We expect 2 mib entry, 8b for the count and sizeof(u64) + 118 // sizeof(MemInfoBlock) contains stack id + MeminfoBlock. 119 EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock))); 120 121 EXPECT_EQ(StackOffset, 336ULL); 122 // We expect 2 stack entries, with 5 frames - 8b for total count, 123 // 2 * (8b for id, 8b for frame count and 5*8b for fake frames) 124 EXPECT_EQ(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8)); 125 126 // ============= Check contents. 127 unsigned char ExpectedSegmentBytes[64] = { 128 0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries 129 0x10, 0, 0, 0, 0, 0, 0, 0, // Start 130 0x20, 0, 0, 0, 0, 0, 0, 0, // End 131 0x10, 0, 0, 0, 0, 0, 0, 0, // Offset 132 0x0C, 0x0, 0xF, 0xF, 0xE, 0xE, // Uuid 133 }; 134 EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 64), 0); 135 136 // Check that the number of entries is 2. 137 EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset), 2ULL); 138 // Check that stack id is set. 139 EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset + 8), FakeIds[0]); 140 141 // Only check a few fields of the first MemInfoBlock. 142 unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = { 143 0x01, 0, 0, 0, // Alloc count 144 0x02, 0, 0, 0, // Total access count 145 }; 146 // Compare contents of 1st MIB after skipping count and stack id. 147 EXPECT_EQ( 148 memcmp(Buffer + MIBOffset + 16, ExpectedMIBBytes, sizeof(MemInfoBlock)), 149 0); 150 // Compare contents of 2nd MIB after skipping count and stack id for the first 151 // and only the id for the second. 152 EXPECT_EQ(memcmp(Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8, 153 ExpectedMIBBytes, sizeof(MemInfoBlock)), 154 0); 155 156 // Check that the number of entries is 2. 157 EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset), 2ULL); 158 // Check that the 1st stack id is set. 159 EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset + 8), 160 FakeIds[0]); 161 // Contents are num pcs, value of each pc - 1. 162 unsigned char ExpectedStackBytes[2][6 * 8] = { 163 { 164 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs 165 0x1, 0, 0, 0, 0, 0, 0, 0, // PC ... 166 0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, 167 0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0, 168 }, 169 { 170 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs 171 0x2, 0, 0, 0, 0, 0, 0, 0, // PC ... 172 0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 173 0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0, 174 }, 175 }; 176 EXPECT_EQ(memcmp(Buffer + StackOffset + 16, ExpectedStackBytes[0], 177 sizeof(ExpectedStackBytes[0])), 178 0); 179 180 // Check that the 2nd stack id is set. 181 EXPECT_EQ( 182 *reinterpret_cast<const u64 *>(Buffer + StackOffset + 8 + 6 * 8 + 8), 183 FakeIds[1]); 184 185 EXPECT_EQ(memcmp(Buffer + StackOffset + 16 + 6 * 8 + 8, ExpectedStackBytes[1], 186 sizeof(ExpectedStackBytes[1])), 187 0); 188 } 189 190 } // namespace 191