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