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