1349cc55cSDimitry Andric #include "memprof/memprof_rawprofile.h" 2349cc55cSDimitry Andric 34824e7fdSDimitry Andric #include <cstdint> 44824e7fdSDimitry Andric #include <memory> 54824e7fdSDimitry Andric 64824e7fdSDimitry Andric #include "profile/MemProfData.inc" 7349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_common.h" 8349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_procmaps.h" 9349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h" 10349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h" 11349cc55cSDimitry Andric #include "gmock/gmock.h" 12349cc55cSDimitry Andric #include "gtest/gtest.h" 13349cc55cSDimitry Andric 14349cc55cSDimitry Andric namespace { 15349cc55cSDimitry Andric 16349cc55cSDimitry Andric using ::__memprof::MIBMapTy; 17349cc55cSDimitry Andric using ::__memprof::SerializeToRawProfile; 18349cc55cSDimitry Andric using ::__sanitizer::MemoryMappedSegment; 19349cc55cSDimitry Andric using ::__sanitizer::MemoryMappingLayoutBase; 20349cc55cSDimitry Andric using ::__sanitizer::StackDepotPut; 21349cc55cSDimitry Andric using ::__sanitizer::StackTrace; 221fd87a68SDimitry Andric using ::llvm::memprof::MemInfoBlock; 23349cc55cSDimitry Andric using ::testing::_; 24349cc55cSDimitry Andric using ::testing::Action; 25349cc55cSDimitry Andric using ::testing::DoAll; 26349cc55cSDimitry Andric using ::testing::Return; 27349cc55cSDimitry Andric using ::testing::SetArgPointee; 28349cc55cSDimitry Andric 29349cc55cSDimitry Andric class MockMemoryMappingLayout final : public MemoryMappingLayoutBase { 30349cc55cSDimitry Andric public: 31349cc55cSDimitry Andric MOCK_METHOD(bool, Next, (MemoryMappedSegment *), (override)); 32349cc55cSDimitry Andric MOCK_METHOD(void, Reset, (), (override)); 33349cc55cSDimitry Andric }; 34349cc55cSDimitry Andric 351fd87a68SDimitry Andric uint64_t PopulateFakeMap(const MemInfoBlock &FakeMIB, uint64_t StackPCBegin, 36349cc55cSDimitry Andric MIBMapTy &FakeMap) { 37349cc55cSDimitry Andric constexpr int kSize = 5; 381fd87a68SDimitry Andric uint64_t array[kSize]; 39349cc55cSDimitry Andric for (int i = 0; i < kSize; i++) { 40349cc55cSDimitry Andric array[i] = StackPCBegin + i; 41349cc55cSDimitry Andric } 42349cc55cSDimitry Andric StackTrace St(array, kSize); 431fd87a68SDimitry Andric uint32_t Id = StackDepotPut(St); 44349cc55cSDimitry Andric 45349cc55cSDimitry Andric InsertOrMerge(Id, FakeMIB, FakeMap); 46349cc55cSDimitry Andric return Id; 47349cc55cSDimitry Andric } 48349cc55cSDimitry Andric 491fd87a68SDimitry Andric template <class T = uint64_t> T Read(char *&Buffer) { 50349cc55cSDimitry Andric static_assert(std::is_pod<T>::value, "Must be a POD type."); 514824e7fdSDimitry Andric assert(reinterpret_cast<size_t>(Buffer) % sizeof(T) == 0 && 524824e7fdSDimitry Andric "Unaligned read!"); 53349cc55cSDimitry Andric T t = *reinterpret_cast<T *>(Buffer); 54349cc55cSDimitry Andric Buffer += sizeof(T); 55349cc55cSDimitry Andric return t; 56349cc55cSDimitry Andric } 57349cc55cSDimitry Andric 58349cc55cSDimitry Andric TEST(MemProf, Basic) { 59349cc55cSDimitry Andric MockMemoryMappingLayout Layout; 60349cc55cSDimitry Andric MemoryMappedSegment FakeSegment; 61349cc55cSDimitry Andric memset(&FakeSegment, 0, sizeof(FakeSegment)); 62349cc55cSDimitry Andric FakeSegment.start = 0x10; 63349cc55cSDimitry Andric FakeSegment.end = 0x20; 64349cc55cSDimitry Andric FakeSegment.offset = 0x10; 65349cc55cSDimitry Andric uint8_t uuid[__sanitizer::kModuleUUIDSize] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE}; 66349cc55cSDimitry Andric memcpy(FakeSegment.uuid, uuid, __sanitizer::kModuleUUIDSize); 67349cc55cSDimitry Andric FakeSegment.protection = 68349cc55cSDimitry Andric __sanitizer::kProtectionExecute | __sanitizer::kProtectionRead; 69349cc55cSDimitry Andric 70349cc55cSDimitry Andric const Action<bool(MemoryMappedSegment *)> SetSegment = 71349cc55cSDimitry Andric DoAll(SetArgPointee<0>(FakeSegment), Return(true)); 72349cc55cSDimitry Andric EXPECT_CALL(Layout, Next(_)) 73349cc55cSDimitry Andric .WillOnce(SetSegment) 74349cc55cSDimitry Andric .WillOnce(Return(false)) 75349cc55cSDimitry Andric .WillOnce(SetSegment) 76349cc55cSDimitry Andric .WillRepeatedly(Return(false)); 77349cc55cSDimitry Andric 78349cc55cSDimitry Andric EXPECT_CALL(Layout, Reset).Times(2); 79349cc55cSDimitry Andric 80349cc55cSDimitry Andric MIBMapTy FakeMap; 81349cc55cSDimitry Andric MemInfoBlock FakeMIB; 82349cc55cSDimitry Andric // Since we want to override the constructor set vals to make it easier to 83349cc55cSDimitry Andric // test. 84349cc55cSDimitry Andric memset(&FakeMIB, 0, sizeof(MemInfoBlock)); 85*81ad6265SDimitry Andric FakeMIB.AllocCount = 0x1; 86*81ad6265SDimitry Andric FakeMIB.TotalAccessCount = 0x2; 87349cc55cSDimitry Andric 881fd87a68SDimitry Andric uint64_t FakeIds[2]; 89349cc55cSDimitry Andric FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap); 90349cc55cSDimitry Andric FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap); 91349cc55cSDimitry Andric 92349cc55cSDimitry Andric char *Ptr = nullptr; 931fd87a68SDimitry Andric uint64_t NumBytes = SerializeToRawProfile(FakeMap, Layout, Ptr); 94349cc55cSDimitry Andric const char *Buffer = Ptr; 95349cc55cSDimitry Andric 96349cc55cSDimitry Andric ASSERT_GT(NumBytes, 0ULL); 97349cc55cSDimitry Andric ASSERT_TRUE(Ptr); 98349cc55cSDimitry Andric 99349cc55cSDimitry Andric // Check the header. 100349cc55cSDimitry Andric EXPECT_THAT(Read(Ptr), MEMPROF_RAW_MAGIC_64); 101349cc55cSDimitry Andric EXPECT_THAT(Read(Ptr), MEMPROF_RAW_VERSION); 1021fd87a68SDimitry Andric const uint64_t TotalSize = Read(Ptr); 1031fd87a68SDimitry Andric const uint64_t SegmentOffset = Read(Ptr); 1041fd87a68SDimitry Andric const uint64_t MIBOffset = Read(Ptr); 1051fd87a68SDimitry Andric const uint64_t StackOffset = Read(Ptr); 106349cc55cSDimitry Andric 1074824e7fdSDimitry Andric // ============= Check sizes and padding. 108349cc55cSDimitry Andric EXPECT_EQ(TotalSize, NumBytes); 1094824e7fdSDimitry Andric EXPECT_EQ(TotalSize % 8, 0ULL); 110349cc55cSDimitry Andric 111349cc55cSDimitry Andric // Should be equal to the size of the raw profile header. 112349cc55cSDimitry Andric EXPECT_EQ(SegmentOffset, 48ULL); 113349cc55cSDimitry Andric 114349cc55cSDimitry Andric // We expect only 1 segment entry, 8b for the count and 56b for SegmentEntry 115349cc55cSDimitry Andric // in memprof_rawprofile.cpp. 116349cc55cSDimitry Andric EXPECT_EQ(MIBOffset - SegmentOffset, 64ULL); 117349cc55cSDimitry Andric 118349cc55cSDimitry Andric EXPECT_EQ(MIBOffset, 112ULL); 1191fd87a68SDimitry Andric // We expect 2 mib entry, 8b for the count and sizeof(uint64_t) + 120349cc55cSDimitry Andric // sizeof(MemInfoBlock) contains stack id + MeminfoBlock. 121349cc55cSDimitry Andric EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock))); 122349cc55cSDimitry Andric 123349cc55cSDimitry Andric EXPECT_EQ(StackOffset, 336ULL); 124349cc55cSDimitry Andric // We expect 2 stack entries, with 5 frames - 8b for total count, 1254824e7fdSDimitry Andric // 2 * (8b for id, 8b for frame count and 5*8b for fake frames). 1264824e7fdSDimitry Andric // Since this is the last section, there may be additional padding at the end 1274824e7fdSDimitry Andric // to make the total profile size 8b aligned. 1284824e7fdSDimitry Andric EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8)); 129349cc55cSDimitry Andric 130349cc55cSDimitry Andric // ============= Check contents. 1311fd87a68SDimitry Andric // The Uuid field is not yet populated on Linux-Elf by the sanitizer procmaps 1321fd87a68SDimitry Andric // library, so we expect it to be filled with 0 for now. 133349cc55cSDimitry Andric unsigned char ExpectedSegmentBytes[64] = { 134349cc55cSDimitry Andric 0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries 135349cc55cSDimitry Andric 0x10, 0, 0, 0, 0, 0, 0, 0, // Start 136349cc55cSDimitry Andric 0x20, 0, 0, 0, 0, 0, 0, 0, // End 137349cc55cSDimitry Andric 0x10, 0, 0, 0, 0, 0, 0, 0, // Offset 1381fd87a68SDimitry Andric 0x0, // Uuid 139349cc55cSDimitry Andric }; 140349cc55cSDimitry Andric EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 64), 0); 141349cc55cSDimitry Andric 142349cc55cSDimitry Andric // Check that the number of entries is 2. 1431fd87a68SDimitry Andric EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset), 2ULL); 144349cc55cSDimitry Andric // Check that stack id is set. 1451fd87a68SDimitry Andric EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset + 8), 1461fd87a68SDimitry Andric FakeIds[0]); 147349cc55cSDimitry Andric 148349cc55cSDimitry Andric // Only check a few fields of the first MemInfoBlock. 149349cc55cSDimitry Andric unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = { 150349cc55cSDimitry Andric 0x01, 0, 0, 0, // Alloc count 151349cc55cSDimitry Andric 0x02, 0, 0, 0, // Total access count 152349cc55cSDimitry Andric }; 153349cc55cSDimitry Andric // Compare contents of 1st MIB after skipping count and stack id. 154349cc55cSDimitry Andric EXPECT_EQ( 155349cc55cSDimitry Andric memcmp(Buffer + MIBOffset + 16, ExpectedMIBBytes, sizeof(MemInfoBlock)), 156349cc55cSDimitry Andric 0); 157349cc55cSDimitry Andric // Compare contents of 2nd MIB after skipping count and stack id for the first 158349cc55cSDimitry Andric // and only the id for the second. 159349cc55cSDimitry Andric EXPECT_EQ(memcmp(Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8, 160349cc55cSDimitry Andric ExpectedMIBBytes, sizeof(MemInfoBlock)), 161349cc55cSDimitry Andric 0); 162349cc55cSDimitry Andric 163349cc55cSDimitry Andric // Check that the number of entries is 2. 1641fd87a68SDimitry Andric EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset), 2ULL); 165349cc55cSDimitry Andric // Check that the 1st stack id is set. 1661fd87a68SDimitry Andric EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8), 167349cc55cSDimitry Andric FakeIds[0]); 168349cc55cSDimitry Andric // Contents are num pcs, value of each pc - 1. 169349cc55cSDimitry Andric unsigned char ExpectedStackBytes[2][6 * 8] = { 170349cc55cSDimitry Andric { 171349cc55cSDimitry Andric 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs 172349cc55cSDimitry Andric 0x1, 0, 0, 0, 0, 0, 0, 0, // PC ... 173349cc55cSDimitry Andric 0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, 174349cc55cSDimitry Andric 0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0, 175349cc55cSDimitry Andric }, 176349cc55cSDimitry Andric { 177349cc55cSDimitry Andric 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs 178349cc55cSDimitry Andric 0x2, 0, 0, 0, 0, 0, 0, 0, // PC ... 179349cc55cSDimitry Andric 0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 180349cc55cSDimitry Andric 0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0, 181349cc55cSDimitry Andric }, 182349cc55cSDimitry Andric }; 183349cc55cSDimitry Andric EXPECT_EQ(memcmp(Buffer + StackOffset + 16, ExpectedStackBytes[0], 184349cc55cSDimitry Andric sizeof(ExpectedStackBytes[0])), 185349cc55cSDimitry Andric 0); 186349cc55cSDimitry Andric 187349cc55cSDimitry Andric // Check that the 2nd stack id is set. 188349cc55cSDimitry Andric EXPECT_EQ( 1891fd87a68SDimitry Andric *reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8 + 6 * 8 + 8), 190349cc55cSDimitry Andric FakeIds[1]); 191349cc55cSDimitry Andric 192349cc55cSDimitry Andric EXPECT_EQ(memcmp(Buffer + StackOffset + 16 + 6 * 8 + 8, ExpectedStackBytes[1], 193349cc55cSDimitry Andric sizeof(ExpectedStackBytes[1])), 194349cc55cSDimitry Andric 0); 195349cc55cSDimitry Andric } 196349cc55cSDimitry Andric 197349cc55cSDimitry Andric } // namespace 198