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