1 | #include "memprof/memprof_rawprofile.h" |
2 | |
3 | #include <cstdint> |
4 | #include <memory> |
5 | |
6 | #include "profile/MemProfData.inc" |
7 | #include "sanitizer_common/sanitizer_array_ref.h" |
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::MIBMapTy; |
18 | using ::__memprof::SerializeToRawProfile; |
19 | using ::__sanitizer::StackDepotPut; |
20 | using ::__sanitizer::StackTrace; |
21 | using ::llvm::memprof::MemInfoBlock; |
22 | |
23 | uint64_t PopulateFakeMap(const MemInfoBlock &FakeMIB, uintptr_t StackPCBegin, |
24 | MIBMapTy &FakeMap) { |
25 | constexpr int kSize = 5; |
26 | uintptr_t array[kSize]; |
27 | for (int i = 0; i < kSize; i++) { |
28 | array[i] = StackPCBegin + i; |
29 | } |
30 | StackTrace St(array, kSize); |
31 | uint32_t Id = StackDepotPut(stack: St); |
32 | |
33 | InsertOrMerge(Id, Block: FakeMIB, Map&: FakeMap); |
34 | return Id; |
35 | } |
36 | |
37 | template <class T = uint64_t> T Read(char *&Buffer) { |
38 | static_assert(std::is_pod<T>::value, "Must be a POD type." ); |
39 | assert(reinterpret_cast<size_t>(Buffer) % sizeof(T) == 0 && |
40 | "Unaligned read!" ); |
41 | T t = *reinterpret_cast<T *>(Buffer); |
42 | Buffer += sizeof(T); |
43 | return t; |
44 | } |
45 | |
46 | TEST(MemProf, Basic) { |
47 | __sanitizer::LoadedModule FakeModule; |
48 | FakeModule.addAddressRange(/*begin=*/beg: 0x10, /*end=*/0x20, /*executable=*/true, |
49 | /*writable=*/false, /*name=*/"" ); |
50 | const char uuid[MEMPROF_BUILDID_MAX_SIZE] = {0xC, 0x0, 0xF, 0xF, 0xE, 0xE}; |
51 | FakeModule.setUuid(uuid, MEMPROF_BUILDID_MAX_SIZE); |
52 | __sanitizer::ArrayRef<__sanitizer::LoadedModule> Modules(&FakeModule, |
53 | (&FakeModule) + 1); |
54 | |
55 | MIBMapTy FakeMap; |
56 | MemInfoBlock FakeMIB; |
57 | // Since we want to override the constructor set vals to make it easier to |
58 | // test. |
59 | memset(s: &FakeMIB, c: 0, n: sizeof(MemInfoBlock)); |
60 | FakeMIB.AllocCount = 0x1; |
61 | FakeMIB.TotalAccessCount = 0x2; |
62 | |
63 | uint64_t FakeIds[2]; |
64 | FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap); |
65 | FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap); |
66 | |
67 | char *Ptr = nullptr; |
68 | uint64_t NumBytes = SerializeToRawProfile(BlockCache&: FakeMap, Modules, Buffer&: Ptr); |
69 | const char *Buffer = Ptr; |
70 | |
71 | ASSERT_GT(NumBytes, 0ULL); |
72 | ASSERT_TRUE(Ptr); |
73 | |
74 | // Check the header. |
75 | EXPECT_THAT(Read(Buffer&: Ptr), MEMPROF_RAW_MAGIC_64); |
76 | EXPECT_THAT(Read(Buffer&: Ptr), MEMPROF_RAW_VERSION); |
77 | const uint64_t TotalSize = Read(Buffer&: Ptr); |
78 | const uint64_t SegmentOffset = Read(Buffer&: Ptr); |
79 | const uint64_t MIBOffset = Read(Buffer&: Ptr); |
80 | const uint64_t StackOffset = Read(Buffer&: Ptr); |
81 | |
82 | // ============= Check sizes and padding. |
83 | EXPECT_EQ(TotalSize, NumBytes); |
84 | EXPECT_EQ(TotalSize % 8, 0ULL); |
85 | |
86 | // Should be equal to the size of the raw profile header. |
87 | EXPECT_EQ(SegmentOffset, 48ULL); |
88 | |
89 | // We expect only 1 segment entry, 8b for the count and 64b for SegmentEntry |
90 | // in memprof_rawprofile.cpp. |
91 | EXPECT_EQ(MIBOffset - SegmentOffset, 72ULL); |
92 | |
93 | EXPECT_EQ(MIBOffset, 120ULL); |
94 | // We expect 2 mib entry, 8b for the count and sizeof(uint64_t) + |
95 | // sizeof(MemInfoBlock) contains stack id + MeminfoBlock. |
96 | EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock))); |
97 | |
98 | EXPECT_EQ(StackOffset, 408ULL); |
99 | // We expect 2 stack entries, with 5 frames - 8b for total count, |
100 | // 2 * (8b for id, 8b for frame count and 5*8b for fake frames). |
101 | // Since this is the last section, there may be additional padding at the end |
102 | // to make the total profile size 8b aligned. |
103 | EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8)); |
104 | |
105 | // ============= Check contents. |
106 | unsigned char ExpectedSegmentBytes[72] = { |
107 | 0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries |
108 | 0x10, 0, 0, 0, 0, 0, 0, 0, // Start |
109 | 0x20, 0, 0, 0, 0, 0, 0, 0, // End |
110 | 0x0, 0, 0, 0, 0, 0, 0, 0, // Offset |
111 | 0x20, 0, 0, 0, 0, 0, 0, 0, // UuidSize |
112 | 0xC, 0x0, 0xF, 0xF, 0xE, 0xE // Uuid |
113 | }; |
114 | EXPECT_EQ(memcmp(s1: Buffer + SegmentOffset, s2: ExpectedSegmentBytes, n: 72), 0); |
115 | |
116 | // Check that the number of entries is 2. |
117 | EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset), 2ULL); |
118 | // Check that stack id is set. |
119 | EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset + 8), |
120 | FakeIds[0]); |
121 | |
122 | // Only check a few fields of the first MemInfoBlock. |
123 | unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = { |
124 | 0x01, 0, 0, 0, // Alloc count |
125 | 0x02, 0, 0, 0, // Total access count |
126 | }; |
127 | // Compare contents of 1st MIB after skipping count and stack id. |
128 | EXPECT_EQ( |
129 | memcmp(s1: Buffer + MIBOffset + 16, s2: ExpectedMIBBytes, n: sizeof(MemInfoBlock)), |
130 | 0); |
131 | // Compare contents of 2nd MIB after skipping count and stack id for the first |
132 | // and only the id for the second. |
133 | EXPECT_EQ(memcmp(s1: Buffer + MIBOffset + 16 + sizeof(MemInfoBlock) + 8, |
134 | s2: ExpectedMIBBytes, n: sizeof(MemInfoBlock)), |
135 | 0); |
136 | |
137 | // Check that the number of entries is 2. |
138 | EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset), 2ULL); |
139 | // Check that the 1st stack id is set. |
140 | EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8), |
141 | FakeIds[0]); |
142 | // Contents are num pcs, value of each pc - 1. |
143 | unsigned char ExpectedStackBytes[2][6 * 8] = { |
144 | { |
145 | 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs |
146 | 0x1, 0, 0, 0, 0, 0, 0, 0, // PC ... |
147 | 0x2, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, |
148 | 0x4, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0, |
149 | }, |
150 | { |
151 | 0x5, 0, 0, 0, 0, 0, 0, 0, // Number of PCs |
152 | 0x2, 0, 0, 0, 0, 0, 0, 0, // PC ... |
153 | 0x3, 0, 0, 0, 0, 0, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, |
154 | 0x5, 0, 0, 0, 0, 0, 0, 0, 0x6, 0, 0, 0, 0, 0, 0, 0, |
155 | }, |
156 | }; |
157 | EXPECT_EQ(memcmp(s1: Buffer + StackOffset + 16, s2: ExpectedStackBytes[0], |
158 | n: sizeof(ExpectedStackBytes[0])), |
159 | 0); |
160 | |
161 | // Check that the 2nd stack id is set. |
162 | EXPECT_EQ( |
163 | *reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8 + 6 * 8 + 8), |
164 | FakeIds[1]); |
165 | |
166 | EXPECT_EQ(memcmp(s1: Buffer + StackOffset + 16 + 6 * 8 + 8, s2: ExpectedStackBytes[1], |
167 | n: sizeof(ExpectedStackBytes[1])), |
168 | 0); |
169 | } |
170 | } // namespace |
171 | |