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