#include "memprof/memprof_rawprofile.h"
#include <cstdint>
#include <memory>
#include "profile/MemProfData.inc"
#include "sanitizer_common/sanitizer_array_ref.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace {
using ::__memprof::MIBMapTy;
using ::__memprof::SerializeToRawProfile;
using ::__sanitizer::StackDepotPut;
using ::__sanitizer::StackTrace;
using ::llvm::memprof::MemInfoBlock;
uint64_t PopulateFakeMap(const MemInfoBlock &FakeMIB, uintptr_t StackPCBegin,
MIBMapTy &FakeMap) {
constexpr int kSize = 5;
uintptr_t array[kSize];
for (int i = 0; i < kSize; i++) {
array[i] = StackPCBegin + i;
}
StackTrace St(array, kSize);
uint32_t Id = StackDepotPut(St);
InsertOrMerge(Id, FakeMIB, FakeMap);
return Id;
}
template <class T = uint64_t> T Read(char *&Buffer) {
static_assert(std::is_pod<T>::value, "Must be a POD type.");
assert(reinterpret_cast<size_t>(Buffer) % sizeof(T) == 0 &&
"Unaligned read!");
T t = *reinterpret_cast<T *>(Buffer);
Buffer += sizeof(T);
return t;
}
TEST(MemProf, Basic) {
__sanitizer::LoadedModule FakeModule;
FakeModule.addAddressRange(/*begin=*/0x10, /*end=*/0x20, /*executable=*/true,
/*writable=*/false, /*name=*/"");
const char uuid[MEMPROF_BUILDID_MAX_SIZE] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE};
FakeModule.setUuid(uuid, MEMPROF_BUILDID_MAX_SIZE);
__sanitizer::ArrayRef<__sanitizer::LoadedModule> Modules(&FakeModule,
(&FakeModule) + 1);
MIBMapTy FakeMap;
MemInfoBlock FakeMIB;
// Since we want to override the constructor set vals to make it easier to
// test.
memset(&FakeMIB, 0, sizeof(MemInfoBlock));
FakeMIB.AllocCount = 0x1;
FakeMIB.TotalAccessCount = 0x2;
uint64_t FakeIds[2];
FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap);
FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap);
char *Ptr = nullptr;
uint64_t NumBytes = SerializeToRawProfile(FakeMap, Modules, Ptr);
const char *Buffer = Ptr;
ASSERT_GT(NumBytes, 0ULL);
ASSERT_TRUE(Ptr);
// Check the header.
EXPECT_THAT(Read(Ptr), MEMPROF_RAW_MAGIC_64);
EXPECT_THAT(Read(Ptr), MEMPROF_RAW_VERSION);
const uint64_t TotalSize = Read(Ptr);
const uint64_t SegmentOffset = Read(Ptr);
const uint64_t MIBOffset = Read(Ptr);
const uint64_t StackOffset = Read(Ptr);
// ============= Check sizes and padding.
EXPECT_EQ(TotalSize, NumBytes);
EXPECT_EQ(TotalSize % 8, 0ULL);
// Should be equal to the size of the raw profile header.
EXPECT_EQ(SegmentOffset, 48ULL);
// We expect only 1 segment entry, 8b for the count and 64b for SegmentEntry
// in memprof_rawprofile.cpp.
EXPECT_EQ(MIBOffset - SegmentOffset, 72ULL);
EXPECT_EQ(MIBOffset, 120ULL);
// We expect 2 mib entry, 8b for the count and sizeof(uint64_t) +
// sizeof(MemInfoBlock) contains stack id + MeminfoBlock.
EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock)));
EXPECT_EQ(StackOffset, 432ULL);
// We expect 2 stack entries, with 5 frames - 8b for total count,
// 2 * (8b for id, 8b for frame count and 5*8b for fake frames).
// Since this is the last section, there may be additional padding at the end
// to make the total profile size 8b aligned.
EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8));
// ============= Check contents.
unsigned char ExpectedSegmentBytes[72] = {
0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries
0x10, 0, 0, 0, 0, 0, 0, 0, // Start
0x20, 0, 0, 0, 0, 0, 0, 0, // End
0x0, 0, 0, 0, 0, 0, 0, 0, // Offset
0x20, 0, 0, 0, 0, 0, 0, 0, // UuidSize
0xC, 0x0, 0xF, 0xF, 0xE, 0xE // Uuid
};
EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 72), 0);
// Check that the number of entries is 2.
EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset), 2ULL);
// Check that stack id is set.
EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset + 8),
FakeIds[0]);
// Only check a few fields of the first MemInfoBlock.
unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = {
0x01, 0, 0, 0, // Alloc count
0x02, 0, 0, 0, // Total access count
};
// Compare contents of 1st MIB after skipping count and stack id.
EXPECT_EQ(
memcmp(Buffer + MIBOffset + 16, ExpectedMIBBytes, sizeof(MemInfoBlock)),
0);
// Compare contents of 2nd MIB after skipping count and stack id for the first
// and only the id for the second.
EXPECT_EQ(memcmp(Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8,
ExpectedMIBBytes, sizeof(MemInfoBlock)),
0);
// Check that the number of entries is 2.
EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset), 2ULL);
// Check that the 1st stack id is set.
EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8),
FakeIds[0]);
// Contents are num pcs, value of each pc - 1.
unsigned char ExpectedStackBytes[2][6 * 8] = {
{
0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs
0x1, 0, 0, 0, 0, 0, 0, 0, // PC ...
0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0,
0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0,
},
{
0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs
0x2, 0, 0, 0, 0, 0, 0, 0, // PC ...
0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0,
0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0,
},
};
EXPECT_EQ(memcmp(Buffer + StackOffset + 16, ExpectedStackBytes[0],
sizeof(ExpectedStackBytes[0])),
0);
// Check that the 2nd stack id is set.
EXPECT_EQ(
*reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8 + 6 * 8 + 8),
FakeIds[1]);
EXPECT_EQ(memcmp(Buffer + StackOffset + 16 + 6 * 8 + 8, ExpectedStackBytes[1],
sizeof(ExpectedStackBytes[1])),
0);
}
} // namespace