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