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" 706c3fb27SDimitry Andric #include "sanitizer_common/sanitizer_array_ref.h" 8349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_common.h" 9349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_procmaps.h" 10349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h" 11349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h" 12349cc55cSDimitry Andric #include "gmock/gmock.h" 13349cc55cSDimitry Andric #include "gtest/gtest.h" 14349cc55cSDimitry Andric 15349cc55cSDimitry Andric namespace { 16349cc55cSDimitry Andric 17349cc55cSDimitry Andric using ::__memprof::MIBMapTy; 18349cc55cSDimitry Andric using ::__memprof::SerializeToRawProfile; 19349cc55cSDimitry Andric using ::__sanitizer::StackDepotPut; 20349cc55cSDimitry Andric using ::__sanitizer::StackTrace; 211fd87a68SDimitry Andric using ::llvm::memprof::MemInfoBlock; 22349cc55cSDimitry Andric 2306c3fb27SDimitry Andric uint64_t PopulateFakeMap(const MemInfoBlock &FakeMIB, uintptr_t StackPCBegin, 24349cc55cSDimitry Andric MIBMapTy &FakeMap) { 25349cc55cSDimitry Andric constexpr int kSize = 5; 2606c3fb27SDimitry Andric uintptr_t array[kSize]; 27349cc55cSDimitry Andric for (int i = 0; i < kSize; i++) { 28349cc55cSDimitry Andric array[i] = StackPCBegin + i; 29349cc55cSDimitry Andric } 30349cc55cSDimitry Andric StackTrace St(array, kSize); 311fd87a68SDimitry Andric uint32_t Id = StackDepotPut(St); 32349cc55cSDimitry Andric 33349cc55cSDimitry Andric InsertOrMerge(Id, FakeMIB, FakeMap); 34349cc55cSDimitry Andric return Id; 35349cc55cSDimitry Andric } 36349cc55cSDimitry Andric 371fd87a68SDimitry Andric template <class T = uint64_t> T Read(char *&Buffer) { 38349cc55cSDimitry Andric static_assert(std::is_pod<T>::value, "Must be a POD type."); 394824e7fdSDimitry Andric assert(reinterpret_cast<size_t>(Buffer) % sizeof(T) == 0 && 404824e7fdSDimitry Andric "Unaligned read!"); 41349cc55cSDimitry Andric T t = *reinterpret_cast<T *>(Buffer); 42349cc55cSDimitry Andric Buffer += sizeof(T); 43349cc55cSDimitry Andric return t; 44349cc55cSDimitry Andric } 45349cc55cSDimitry Andric 46349cc55cSDimitry Andric TEST(MemProf, Basic) { 4706c3fb27SDimitry Andric __sanitizer::LoadedModule FakeModule; 4806c3fb27SDimitry Andric FakeModule.addAddressRange(/*begin=*/0x10, /*end=*/0x20, /*executable=*/true, 4906c3fb27SDimitry Andric /*writable=*/false, /*name=*/""); 5006c3fb27SDimitry Andric const char uuid[MEMPROF_BUILDID_MAX_SIZE] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE}; 5106c3fb27SDimitry Andric FakeModule.setUuid(uuid, MEMPROF_BUILDID_MAX_SIZE); 5206c3fb27SDimitry Andric __sanitizer::ArrayRef<__sanitizer::LoadedModule> Modules(&FakeModule, 5306c3fb27SDimitry Andric (&FakeModule) + 1); 54349cc55cSDimitry Andric 55349cc55cSDimitry Andric MIBMapTy FakeMap; 56349cc55cSDimitry Andric MemInfoBlock FakeMIB; 57349cc55cSDimitry Andric // Since we want to override the constructor set vals to make it easier to 58349cc55cSDimitry Andric // test. 59349cc55cSDimitry Andric memset(&FakeMIB, 0, sizeof(MemInfoBlock)); 6081ad6265SDimitry Andric FakeMIB.AllocCount = 0x1; 6181ad6265SDimitry Andric FakeMIB.TotalAccessCount = 0x2; 62349cc55cSDimitry Andric 631fd87a68SDimitry Andric uint64_t FakeIds[2]; 64349cc55cSDimitry Andric FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap); 65349cc55cSDimitry Andric FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap); 66349cc55cSDimitry Andric 67349cc55cSDimitry Andric char *Ptr = nullptr; 6806c3fb27SDimitry Andric uint64_t NumBytes = SerializeToRawProfile(FakeMap, Modules, Ptr); 69349cc55cSDimitry Andric const char *Buffer = Ptr; 70349cc55cSDimitry Andric 71349cc55cSDimitry Andric ASSERT_GT(NumBytes, 0ULL); 72349cc55cSDimitry Andric ASSERT_TRUE(Ptr); 73349cc55cSDimitry Andric 74349cc55cSDimitry Andric // Check the header. 75349cc55cSDimitry Andric EXPECT_THAT(Read(Ptr), MEMPROF_RAW_MAGIC_64); 76349cc55cSDimitry Andric EXPECT_THAT(Read(Ptr), MEMPROF_RAW_VERSION); 771fd87a68SDimitry Andric const uint64_t TotalSize = Read(Ptr); 781fd87a68SDimitry Andric const uint64_t SegmentOffset = Read(Ptr); 791fd87a68SDimitry Andric const uint64_t MIBOffset = Read(Ptr); 801fd87a68SDimitry Andric const uint64_t StackOffset = Read(Ptr); 81349cc55cSDimitry Andric 824824e7fdSDimitry Andric // ============= Check sizes and padding. 83349cc55cSDimitry Andric EXPECT_EQ(TotalSize, NumBytes); 844824e7fdSDimitry Andric EXPECT_EQ(TotalSize % 8, 0ULL); 85349cc55cSDimitry Andric 86349cc55cSDimitry Andric // Should be equal to the size of the raw profile header. 87349cc55cSDimitry Andric EXPECT_EQ(SegmentOffset, 48ULL); 88349cc55cSDimitry Andric 8906c3fb27SDimitry Andric // We expect only 1 segment entry, 8b for the count and 64b for SegmentEntry 90349cc55cSDimitry Andric // in memprof_rawprofile.cpp. 9106c3fb27SDimitry Andric EXPECT_EQ(MIBOffset - SegmentOffset, 72ULL); 92349cc55cSDimitry Andric 9306c3fb27SDimitry Andric EXPECT_EQ(MIBOffset, 120ULL); 941fd87a68SDimitry Andric // We expect 2 mib entry, 8b for the count and sizeof(uint64_t) + 95349cc55cSDimitry Andric // sizeof(MemInfoBlock) contains stack id + MeminfoBlock. 96349cc55cSDimitry Andric EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock))); 97349cc55cSDimitry Andric 98*0fca6ea1SDimitry Andric EXPECT_EQ(StackOffset, 432ULL); 99349cc55cSDimitry Andric // We expect 2 stack entries, with 5 frames - 8b for total count, 1004824e7fdSDimitry Andric // 2 * (8b for id, 8b for frame count and 5*8b for fake frames). 1014824e7fdSDimitry Andric // Since this is the last section, there may be additional padding at the end 1024824e7fdSDimitry Andric // to make the total profile size 8b aligned. 1034824e7fdSDimitry Andric EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8)); 104349cc55cSDimitry Andric 105349cc55cSDimitry Andric // ============= Check contents. 10606c3fb27SDimitry Andric unsigned char ExpectedSegmentBytes[72] = { 107349cc55cSDimitry Andric 0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries 108349cc55cSDimitry Andric 0x10, 0, 0, 0, 0, 0, 0, 0, // Start 109349cc55cSDimitry Andric 0x20, 0, 0, 0, 0, 0, 0, 0, // End 11006c3fb27SDimitry Andric 0x0, 0, 0, 0, 0, 0, 0, 0, // Offset 11106c3fb27SDimitry Andric 0x20, 0, 0, 0, 0, 0, 0, 0, // UuidSize 11206c3fb27SDimitry Andric 0xC, 0x0, 0xF, 0xF, 0xE, 0xE // Uuid 113349cc55cSDimitry Andric }; 11406c3fb27SDimitry Andric EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 72), 0); 115349cc55cSDimitry Andric 116349cc55cSDimitry Andric // Check that the number of entries is 2. 1171fd87a68SDimitry Andric EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset), 2ULL); 118349cc55cSDimitry Andric // Check that stack id is set. 1191fd87a68SDimitry Andric EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset + 8), 1201fd87a68SDimitry Andric FakeIds[0]); 121349cc55cSDimitry Andric 122349cc55cSDimitry Andric // Only check a few fields of the first MemInfoBlock. 123349cc55cSDimitry Andric unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = { 124349cc55cSDimitry Andric 0x01, 0, 0, 0, // Alloc count 125349cc55cSDimitry Andric 0x02, 0, 0, 0, // Total access count 126349cc55cSDimitry Andric }; 127349cc55cSDimitry Andric // Compare contents of 1st MIB after skipping count and stack id. 128349cc55cSDimitry Andric EXPECT_EQ( 129349cc55cSDimitry Andric memcmp(Buffer + MIBOffset + 16, ExpectedMIBBytes, sizeof(MemInfoBlock)), 130349cc55cSDimitry Andric 0); 131349cc55cSDimitry Andric // Compare contents of 2nd MIB after skipping count and stack id for the first 132349cc55cSDimitry Andric // and only the id for the second. 133349cc55cSDimitry Andric EXPECT_EQ(memcmp(Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8, 134349cc55cSDimitry Andric ExpectedMIBBytes, sizeof(MemInfoBlock)), 135349cc55cSDimitry Andric 0); 136349cc55cSDimitry Andric 137349cc55cSDimitry Andric // Check that the number of entries is 2. 1381fd87a68SDimitry Andric EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset), 2ULL); 139349cc55cSDimitry Andric // Check that the 1st stack id is set. 1401fd87a68SDimitry Andric EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8), 141349cc55cSDimitry Andric FakeIds[0]); 142349cc55cSDimitry Andric // Contents are num pcs, value of each pc - 1. 143349cc55cSDimitry Andric unsigned char ExpectedStackBytes[2][6 * 8] = { 144349cc55cSDimitry Andric { 145349cc55cSDimitry Andric 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs 146349cc55cSDimitry Andric 0x1, 0, 0, 0, 0, 0, 0, 0, // PC ... 147349cc55cSDimitry Andric 0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, 148349cc55cSDimitry Andric 0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0, 149349cc55cSDimitry Andric }, 150349cc55cSDimitry Andric { 151349cc55cSDimitry Andric 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs 152349cc55cSDimitry Andric 0x2, 0, 0, 0, 0, 0, 0, 0, // PC ... 153349cc55cSDimitry Andric 0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 154349cc55cSDimitry Andric 0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0, 155349cc55cSDimitry Andric }, 156349cc55cSDimitry Andric }; 157349cc55cSDimitry Andric EXPECT_EQ(memcmp(Buffer + StackOffset + 16, ExpectedStackBytes[0], 158349cc55cSDimitry Andric sizeof(ExpectedStackBytes[0])), 159349cc55cSDimitry Andric 0); 160349cc55cSDimitry Andric 161349cc55cSDimitry Andric // Check that the 2nd stack id is set. 162349cc55cSDimitry Andric EXPECT_EQ( 1631fd87a68SDimitry Andric *reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8 + 6 * 8 + 8), 164349cc55cSDimitry Andric FakeIds[1]); 165349cc55cSDimitry Andric 166349cc55cSDimitry Andric EXPECT_EQ(memcmp(Buffer + StackOffset + 16 + 6 * 8 + 8, ExpectedStackBytes[1], 167349cc55cSDimitry Andric sizeof(ExpectedStackBytes[1])), 168349cc55cSDimitry Andric 0); 169349cc55cSDimitry Andric } 170349cc55cSDimitry Andric } // namespace 171