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