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