xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/memprof/tests/rawprofile.cpp (revision 4824e7fd18a1223177218d4aec1b3c6c5c4a444e)
1349cc55cSDimitry Andric #include "memprof/memprof_rawprofile.h"
2349cc55cSDimitry Andric 
3*4824e7fdSDimitry Andric #include <cstdint>
4*4824e7fdSDimitry Andric #include <memory>
5*4824e7fdSDimitry Andric 
6349cc55cSDimitry Andric #include "memprof/memprof_meminfoblock.h"
7*4824e7fdSDimitry Andric #include "profile/MemProfData.inc"
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::MemInfoBlock;
18349cc55cSDimitry Andric using ::__memprof::MIBMapTy;
19349cc55cSDimitry Andric using ::__memprof::SerializeToRawProfile;
20349cc55cSDimitry Andric using ::__sanitizer::MemoryMappedSegment;
21349cc55cSDimitry Andric using ::__sanitizer::MemoryMappingLayoutBase;
22349cc55cSDimitry Andric using ::__sanitizer::StackDepotPut;
23349cc55cSDimitry Andric using ::__sanitizer::StackTrace;
24349cc55cSDimitry Andric using ::testing::_;
25349cc55cSDimitry Andric using ::testing::Action;
26349cc55cSDimitry Andric using ::testing::DoAll;
27349cc55cSDimitry Andric using ::testing::Return;
28349cc55cSDimitry Andric using ::testing::SetArgPointee;
29349cc55cSDimitry Andric 
30349cc55cSDimitry Andric class MockMemoryMappingLayout final : public MemoryMappingLayoutBase {
31349cc55cSDimitry Andric public:
32349cc55cSDimitry Andric   MOCK_METHOD(bool, Next, (MemoryMappedSegment *), (override));
33349cc55cSDimitry Andric   MOCK_METHOD(void, Reset, (), (override));
34349cc55cSDimitry Andric };
35349cc55cSDimitry Andric 
36349cc55cSDimitry Andric u64 PopulateFakeMap(const MemInfoBlock &FakeMIB, uptr StackPCBegin,
37349cc55cSDimitry Andric                     MIBMapTy &FakeMap) {
38349cc55cSDimitry Andric   constexpr int kSize = 5;
39349cc55cSDimitry Andric   uptr array[kSize];
40349cc55cSDimitry Andric   for (int i = 0; i < kSize; i++) {
41349cc55cSDimitry Andric     array[i] = StackPCBegin + i;
42349cc55cSDimitry Andric   }
43349cc55cSDimitry Andric   StackTrace St(array, kSize);
44349cc55cSDimitry Andric   u32 Id = StackDepotPut(St);
45349cc55cSDimitry Andric 
46349cc55cSDimitry Andric   InsertOrMerge(Id, FakeMIB, FakeMap);
47349cc55cSDimitry Andric   return Id;
48349cc55cSDimitry Andric }
49349cc55cSDimitry Andric 
50349cc55cSDimitry Andric template <class T = u64> T Read(char *&Buffer) {
51349cc55cSDimitry Andric   static_assert(std::is_pod<T>::value, "Must be a POD type.");
52*4824e7fdSDimitry Andric   assert(reinterpret_cast<size_t>(Buffer) % sizeof(T) == 0 &&
53*4824e7fdSDimitry Andric          "Unaligned read!");
54349cc55cSDimitry Andric   T t = *reinterpret_cast<T *>(Buffer);
55349cc55cSDimitry Andric   Buffer += sizeof(T);
56349cc55cSDimitry Andric   return t;
57349cc55cSDimitry Andric }
58349cc55cSDimitry Andric 
59349cc55cSDimitry Andric TEST(MemProf, Basic) {
60349cc55cSDimitry Andric   MockMemoryMappingLayout Layout;
61349cc55cSDimitry Andric   MemoryMappedSegment FakeSegment;
62349cc55cSDimitry Andric   memset(&FakeSegment, 0, sizeof(FakeSegment));
63349cc55cSDimitry Andric   FakeSegment.start = 0x10;
64349cc55cSDimitry Andric   FakeSegment.end = 0x20;
65349cc55cSDimitry Andric   FakeSegment.offset = 0x10;
66349cc55cSDimitry Andric   uint8_t uuid[__sanitizer::kModuleUUIDSize] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE};
67349cc55cSDimitry Andric   memcpy(FakeSegment.uuid, uuid, __sanitizer::kModuleUUIDSize);
68349cc55cSDimitry Andric   FakeSegment.protection =
69349cc55cSDimitry Andric       __sanitizer::kProtectionExecute | __sanitizer::kProtectionRead;
70349cc55cSDimitry Andric 
71349cc55cSDimitry Andric   const Action<bool(MemoryMappedSegment *)> SetSegment =
72349cc55cSDimitry Andric       DoAll(SetArgPointee<0>(FakeSegment), Return(true));
73349cc55cSDimitry Andric   EXPECT_CALL(Layout, Next(_))
74349cc55cSDimitry Andric       .WillOnce(SetSegment)
75349cc55cSDimitry Andric       .WillOnce(Return(false))
76349cc55cSDimitry Andric       .WillOnce(SetSegment)
77349cc55cSDimitry Andric       .WillRepeatedly(Return(false));
78349cc55cSDimitry Andric 
79349cc55cSDimitry Andric   EXPECT_CALL(Layout, Reset).Times(2);
80349cc55cSDimitry Andric 
81349cc55cSDimitry Andric   MIBMapTy FakeMap;
82349cc55cSDimitry Andric   MemInfoBlock FakeMIB;
83349cc55cSDimitry Andric   // Since we want to override the constructor set vals to make it easier to
84349cc55cSDimitry Andric   // test.
85349cc55cSDimitry Andric   memset(&FakeMIB, 0, sizeof(MemInfoBlock));
86349cc55cSDimitry Andric   FakeMIB.alloc_count = 0x1;
87349cc55cSDimitry Andric   FakeMIB.total_access_count = 0x2;
88349cc55cSDimitry Andric 
89349cc55cSDimitry Andric   u64 FakeIds[2];
90349cc55cSDimitry Andric   FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap);
91349cc55cSDimitry Andric   FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap);
92349cc55cSDimitry Andric 
93349cc55cSDimitry Andric   char *Ptr = nullptr;
94349cc55cSDimitry Andric   u64 NumBytes = SerializeToRawProfile(FakeMap, Layout, Ptr);
95349cc55cSDimitry Andric   const char *Buffer = Ptr;
96349cc55cSDimitry Andric 
97349cc55cSDimitry Andric   ASSERT_GT(NumBytes, 0ULL);
98349cc55cSDimitry Andric   ASSERT_TRUE(Ptr);
99349cc55cSDimitry Andric 
100349cc55cSDimitry Andric   // Check the header.
101349cc55cSDimitry Andric   EXPECT_THAT(Read(Ptr), MEMPROF_RAW_MAGIC_64);
102349cc55cSDimitry Andric   EXPECT_THAT(Read(Ptr), MEMPROF_RAW_VERSION);
103349cc55cSDimitry Andric   const u64 TotalSize = Read(Ptr);
104349cc55cSDimitry Andric   const u64 SegmentOffset = Read(Ptr);
105349cc55cSDimitry Andric   const u64 MIBOffset = Read(Ptr);
106349cc55cSDimitry Andric   const u64 StackOffset = Read(Ptr);
107349cc55cSDimitry Andric 
108*4824e7fdSDimitry Andric   // ============= Check sizes and padding.
109349cc55cSDimitry Andric   EXPECT_EQ(TotalSize, NumBytes);
110*4824e7fdSDimitry Andric   EXPECT_EQ(TotalSize % 8, 0ULL);
111349cc55cSDimitry Andric 
112349cc55cSDimitry Andric   // Should be equal to the size of the raw profile header.
113349cc55cSDimitry Andric   EXPECT_EQ(SegmentOffset, 48ULL);
114349cc55cSDimitry Andric 
115349cc55cSDimitry Andric   // We expect only 1 segment entry, 8b for the count and 56b for SegmentEntry
116349cc55cSDimitry Andric   // in memprof_rawprofile.cpp.
117349cc55cSDimitry Andric   EXPECT_EQ(MIBOffset - SegmentOffset, 64ULL);
118349cc55cSDimitry Andric 
119349cc55cSDimitry Andric   EXPECT_EQ(MIBOffset, 112ULL);
120349cc55cSDimitry Andric   // We expect 2 mib entry, 8b for the count and sizeof(u64) +
121349cc55cSDimitry Andric   // sizeof(MemInfoBlock) contains stack id + MeminfoBlock.
122349cc55cSDimitry Andric   EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock)));
123349cc55cSDimitry Andric 
124349cc55cSDimitry Andric   EXPECT_EQ(StackOffset, 336ULL);
125349cc55cSDimitry Andric   // We expect 2 stack entries, with 5 frames - 8b for total count,
126*4824e7fdSDimitry Andric   // 2 * (8b for id, 8b for frame count and 5*8b for fake frames).
127*4824e7fdSDimitry Andric   // Since this is the last section, there may be additional padding at the end
128*4824e7fdSDimitry Andric   // to make the total profile size 8b aligned.
129*4824e7fdSDimitry Andric   EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8));
130349cc55cSDimitry Andric 
131349cc55cSDimitry Andric   // ============= Check contents.
132349cc55cSDimitry Andric   unsigned char ExpectedSegmentBytes[64] = {
133349cc55cSDimitry Andric       0x01, 0,   0,   0,   0,   0,   0, 0, // Number of entries
134349cc55cSDimitry Andric       0x10, 0,   0,   0,   0,   0,   0, 0, // Start
135349cc55cSDimitry Andric       0x20, 0,   0,   0,   0,   0,   0, 0, // End
136349cc55cSDimitry Andric       0x10, 0,   0,   0,   0,   0,   0, 0, // Offset
137349cc55cSDimitry Andric       0x0C, 0x0, 0xF, 0xF, 0xE, 0xE,       // Uuid
138349cc55cSDimitry Andric   };
139349cc55cSDimitry Andric   EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 64), 0);
140349cc55cSDimitry Andric 
141349cc55cSDimitry Andric   // Check that the number of entries is 2.
142349cc55cSDimitry Andric   EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset), 2ULL);
143349cc55cSDimitry Andric   // Check that stack id is set.
144349cc55cSDimitry Andric   EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset + 8), FakeIds[0]);
145349cc55cSDimitry Andric 
146349cc55cSDimitry Andric   // Only check a few fields of the first MemInfoBlock.
147349cc55cSDimitry Andric   unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = {
148349cc55cSDimitry Andric       0x01, 0, 0, 0, // Alloc count
149349cc55cSDimitry Andric       0x02, 0, 0, 0, // Total access count
150349cc55cSDimitry Andric   };
151349cc55cSDimitry Andric   // Compare contents of 1st MIB after skipping count and stack id.
152349cc55cSDimitry Andric   EXPECT_EQ(
153349cc55cSDimitry Andric       memcmp(Buffer + MIBOffset + 16, ExpectedMIBBytes, sizeof(MemInfoBlock)),
154349cc55cSDimitry Andric       0);
155349cc55cSDimitry Andric   // Compare contents of 2nd MIB after skipping count and stack id for the first
156349cc55cSDimitry Andric   // and only the id for the second.
157349cc55cSDimitry Andric   EXPECT_EQ(memcmp(Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8,
158349cc55cSDimitry Andric                    ExpectedMIBBytes, sizeof(MemInfoBlock)),
159349cc55cSDimitry Andric             0);
160349cc55cSDimitry Andric 
161349cc55cSDimitry Andric   // Check that the number of entries is 2.
162349cc55cSDimitry Andric   EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset), 2ULL);
163349cc55cSDimitry Andric   // Check that the 1st stack id is set.
164349cc55cSDimitry Andric   EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset + 8),
165349cc55cSDimitry Andric             FakeIds[0]);
166349cc55cSDimitry Andric   // Contents are num pcs, value of each pc - 1.
167349cc55cSDimitry Andric   unsigned char ExpectedStackBytes[2][6 * 8] = {
168349cc55cSDimitry Andric       {
169349cc55cSDimitry Andric           0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs
170349cc55cSDimitry Andric           0x1, 0, 0, 0, 0, 0, 0, 0, // PC ...
171349cc55cSDimitry Andric           0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0,
172349cc55cSDimitry Andric           0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0,
173349cc55cSDimitry Andric       },
174349cc55cSDimitry Andric       {
175349cc55cSDimitry Andric           0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs
176349cc55cSDimitry Andric           0x2, 0, 0, 0, 0, 0, 0, 0, // PC ...
177349cc55cSDimitry Andric           0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0,
178349cc55cSDimitry Andric           0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0,
179349cc55cSDimitry Andric       },
180349cc55cSDimitry Andric   };
181349cc55cSDimitry Andric   EXPECT_EQ(memcmp(Buffer + StackOffset + 16, ExpectedStackBytes[0],
182349cc55cSDimitry Andric                    sizeof(ExpectedStackBytes[0])),
183349cc55cSDimitry Andric             0);
184349cc55cSDimitry Andric 
185349cc55cSDimitry Andric   // Check that the 2nd stack id is set.
186349cc55cSDimitry Andric   EXPECT_EQ(
187349cc55cSDimitry Andric       *reinterpret_cast<const u64 *>(Buffer + StackOffset + 8 + 6 * 8 + 8),
188349cc55cSDimitry Andric       FakeIds[1]);
189349cc55cSDimitry Andric 
190349cc55cSDimitry Andric   EXPECT_EQ(memcmp(Buffer + StackOffset + 16 + 6 * 8 + 8, ExpectedStackBytes[1],
191349cc55cSDimitry Andric                    sizeof(ExpectedStackBytes[1])),
192349cc55cSDimitry Andric             0);
193349cc55cSDimitry Andric }
194349cc55cSDimitry Andric 
195349cc55cSDimitry Andric } // namespace
196