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   T t = *reinterpret_cast<T *>(Buffer);
53   Buffer += sizeof(T);
54   return t;
55 }
56 
57 TEST(MemProf, Basic) {
58   MockMemoryMappingLayout Layout;
59   MemoryMappedSegment FakeSegment;
60   memset(&FakeSegment, 0, sizeof(FakeSegment));
61   FakeSegment.start = 0x10;
62   FakeSegment.end = 0x20;
63   FakeSegment.offset = 0x10;
64   uint8_t uuid[__sanitizer::kModuleUUIDSize] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE};
65   memcpy(FakeSegment.uuid, uuid, __sanitizer::kModuleUUIDSize);
66   FakeSegment.protection =
67       __sanitizer::kProtectionExecute | __sanitizer::kProtectionRead;
68 
69   const Action<bool(MemoryMappedSegment *)> SetSegment =
70       DoAll(SetArgPointee<0>(FakeSegment), Return(true));
71   EXPECT_CALL(Layout, Next(_))
72       .WillOnce(SetSegment)
73       .WillOnce(Return(false))
74       .WillOnce(SetSegment)
75       .WillRepeatedly(Return(false));
76 
77   EXPECT_CALL(Layout, Reset).Times(2);
78 
79   MIBMapTy FakeMap;
80   MemInfoBlock FakeMIB;
81   // Since we want to override the constructor set vals to make it easier to
82   // test.
83   memset(&FakeMIB, 0, sizeof(MemInfoBlock));
84   FakeMIB.alloc_count = 0x1;
85   FakeMIB.total_access_count = 0x2;
86 
87   u64 FakeIds[2];
88   FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap);
89   FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap);
90 
91   char *Ptr = nullptr;
92   u64 NumBytes = SerializeToRawProfile(FakeMap, Layout, Ptr);
93   const char *Buffer = Ptr;
94 
95   ASSERT_GT(NumBytes, 0ULL);
96   ASSERT_TRUE(Ptr);
97 
98   // Check the header.
99   EXPECT_THAT(Read(Ptr), MEMPROF_RAW_MAGIC_64);
100   EXPECT_THAT(Read(Ptr), MEMPROF_RAW_VERSION);
101   const u64 TotalSize = Read(Ptr);
102   const u64 SegmentOffset = Read(Ptr);
103   const u64 MIBOffset = Read(Ptr);
104   const u64 StackOffset = Read(Ptr);
105 
106   // ============= Check sizes.
107   EXPECT_EQ(TotalSize, NumBytes);
108 
109   // Should be equal to the size of the raw profile header.
110   EXPECT_EQ(SegmentOffset, 48ULL);
111 
112   // We expect only 1 segment entry, 8b for the count and 56b for SegmentEntry
113   // in memprof_rawprofile.cpp.
114   EXPECT_EQ(MIBOffset - SegmentOffset, 64ULL);
115 
116   EXPECT_EQ(MIBOffset, 112ULL);
117   // We expect 2 mib entry, 8b for the count and sizeof(u64) +
118   // sizeof(MemInfoBlock) contains stack id + MeminfoBlock.
119   EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock)));
120 
121   EXPECT_EQ(StackOffset, 336ULL);
122   // We expect 2 stack entries, with 5 frames - 8b for total count,
123   // 2 * (8b for id, 8b for frame count and 5*8b for fake frames)
124   EXPECT_EQ(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8));
125 
126   // ============= Check contents.
127   unsigned char ExpectedSegmentBytes[64] = {
128       0x01, 0,   0,   0,   0,   0,   0, 0, // Number of entries
129       0x10, 0,   0,   0,   0,   0,   0, 0, // Start
130       0x20, 0,   0,   0,   0,   0,   0, 0, // End
131       0x10, 0,   0,   0,   0,   0,   0, 0, // Offset
132       0x0C, 0x0, 0xF, 0xF, 0xE, 0xE,       // Uuid
133   };
134   EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 64), 0);
135 
136   // Check that the number of entries is 2.
137   EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset), 2ULL);
138   // Check that stack id is set.
139   EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset + 8), FakeIds[0]);
140 
141   // Only check a few fields of the first MemInfoBlock.
142   unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = {
143       0x01, 0, 0, 0, // Alloc count
144       0x02, 0, 0, 0, // Total access count
145   };
146   // Compare contents of 1st MIB after skipping count and stack id.
147   EXPECT_EQ(
148       memcmp(Buffer + MIBOffset + 16, ExpectedMIBBytes, sizeof(MemInfoBlock)),
149       0);
150   // Compare contents of 2nd MIB after skipping count and stack id for the first
151   // and only the id for the second.
152   EXPECT_EQ(memcmp(Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8,
153                    ExpectedMIBBytes, sizeof(MemInfoBlock)),
154             0);
155 
156   // Check that the number of entries is 2.
157   EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset), 2ULL);
158   // Check that the 1st stack id is set.
159   EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset + 8),
160             FakeIds[0]);
161   // Contents are num pcs, value of each pc - 1.
162   unsigned char ExpectedStackBytes[2][6 * 8] = {
163       {
164           0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs
165           0x1, 0, 0, 0, 0, 0, 0, 0, // PC ...
166           0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0,
167           0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0,
168       },
169       {
170           0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs
171           0x2, 0, 0, 0, 0, 0, 0, 0, // PC ...
172           0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0,
173           0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0,
174       },
175   };
176   EXPECT_EQ(memcmp(Buffer + StackOffset + 16, ExpectedStackBytes[0],
177                    sizeof(ExpectedStackBytes[0])),
178             0);
179 
180   // Check that the 2nd stack id is set.
181   EXPECT_EQ(
182       *reinterpret_cast<const u64 *>(Buffer + StackOffset + 8 + 6 * 8 + 8),
183       FakeIds[1]);
184 
185   EXPECT_EQ(memcmp(Buffer + StackOffset + 16 + 6 * 8 + 8, ExpectedStackBytes[1],
186                    sizeof(ExpectedStackBytes[1])),
187             0);
188 }
189 
190 } // namespace
191