1 | //===-- LinuxProcMapsTest.cpp ---------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "gmock/gmock.h" |
10 | #include "gtest/gtest.h" |
11 | |
12 | #include "Plugins/Process/Utility/LinuxProcMaps.h" |
13 | #include "lldb/Target/MemoryRegionInfo.h" |
14 | #include "lldb/Utility/Status.h" |
15 | #include <tuple> |
16 | |
17 | using namespace lldb_private; |
18 | |
19 | typedef std::tuple<const char *, MemoryRegionInfos, const char *> |
20 | LinuxProcMapsTestParams; |
21 | |
22 | // Wrapper for convenience because Range is usually begin, size |
23 | static MemoryRegionInfo::RangeType make_range(lldb::addr_t begin, |
24 | lldb::addr_t end) { |
25 | MemoryRegionInfo::RangeType range(begin, 0); |
26 | range.SetRangeEnd(end); |
27 | return range; |
28 | } |
29 | |
30 | class LinuxProcMapsTestFixture |
31 | : public ::testing::TestWithParam<LinuxProcMapsTestParams> { |
32 | protected: |
33 | Status error; |
34 | std::string err_str; |
35 | MemoryRegionInfos regions; |
36 | LinuxMapCallback callback; |
37 | |
38 | void SetUp() override { |
39 | callback = [this](llvm::Expected<MemoryRegionInfo> Info) { |
40 | if (Info) { |
41 | err_str.clear(); |
42 | regions.push_back(x: *Info); |
43 | return true; |
44 | } |
45 | |
46 | err_str = toString(E: Info.takeError()); |
47 | return false; |
48 | }; |
49 | } |
50 | |
51 | void check_regions(LinuxProcMapsTestParams params) { |
52 | EXPECT_THAT(std::get<1>(params), testing::ContainerEq(regions)); |
53 | ASSERT_EQ(std::get<2>(params), err_str); |
54 | } |
55 | }; |
56 | |
57 | TEST_P(LinuxProcMapsTestFixture, ParseMapRegions) { |
58 | auto params = GetParam(); |
59 | ParseLinuxMapRegions(linux_map: std::get<0>(t&: params), callback); |
60 | check_regions(params); |
61 | } |
62 | |
63 | // Note: ConstString("") != ConstString(nullptr) |
64 | // When a region has no name, it will have the latter in the MemoryRegionInfo |
65 | INSTANTIATE_TEST_SUITE_P( |
66 | ProcMapTests, LinuxProcMapsTestFixture, |
67 | ::testing::Values( |
68 | // Nothing in nothing out |
69 | std::make_tuple("" , MemoryRegionInfos{}, "" ), |
70 | // Various formatting error conditions |
71 | std::make_tuple("55a4512f7000/55a451b68000 rw-p 00000000 00:00 0" , |
72 | MemoryRegionInfos{}, |
73 | "malformed /proc/{pid}/maps entry, missing dash " |
74 | "between address range" ), |
75 | std::make_tuple("0-0 rw" , MemoryRegionInfos{}, |
76 | "malformed /proc/{pid}/maps entry, missing some " |
77 | "portion of permissions" ), |
78 | std::make_tuple("0-0 z--p 00000000 00:00 0" , MemoryRegionInfos{}, |
79 | "unexpected /proc/{pid}/maps read permission char" ), |
80 | std::make_tuple("0-0 rz-p 00000000 00:00 0" , MemoryRegionInfos{}, |
81 | "unexpected /proc/{pid}/maps write permission char" ), |
82 | std::make_tuple("0-0 rwzp 00000000 00:00 0" , MemoryRegionInfos{}, |
83 | "unexpected /proc/{pid}/maps exec permission char" ), |
84 | // Stops at first parsing error |
85 | std::make_tuple( |
86 | "0-1 rw-p 00000000 00:00 0 [abc]\n" |
87 | "0-0 rwzp 00000000 00:00 0\n" |
88 | "2-3 r-xp 00000000 00:00 0 [def]\n" , |
89 | MemoryRegionInfos{ |
90 | MemoryRegionInfo(make_range(0, 1), MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
91 | MemoryRegionInfo::eNo, |
92 | MemoryRegionInfo::eYes, ConstString("[abc]" ), |
93 | MemoryRegionInfo::eDontKnow, 0, |
94 | MemoryRegionInfo::eDontKnow, |
95 | MemoryRegionInfo::eDontKnow), |
96 | }, |
97 | "unexpected /proc/{pid}/maps exec permission char" ), |
98 | // Single entry |
99 | std::make_tuple( |
100 | "55a4512f7000-55a451b68000 rw-p 00000000 00:00 0 [heap]" , |
101 | MemoryRegionInfos{ |
102 | MemoryRegionInfo( |
103 | make_range(0x55a4512f7000, 0x55a451b68000), |
104 | MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
105 | MemoryRegionInfo::eNo, |
106 | MemoryRegionInfo::eYes, |
107 | ConstString("[heap]" ), MemoryRegionInfo::eDontKnow, 0, |
108 | MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow), |
109 | }, |
110 | "" ), |
111 | // Multiple entries |
112 | std::make_tuple( |
113 | "7fc090021000-7fc094000000 ---p 00000000 00:00 0\n" |
114 | "7fc094000000-7fc094a00000 ---s 00000000 00:00 0\n" |
115 | "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 " |
116 | "[vsyscall]" , |
117 | MemoryRegionInfos{ |
118 | MemoryRegionInfo( |
119 | make_range(0x7fc090021000, 0x7fc094000000), |
120 | MemoryRegionInfo::eNo, MemoryRegionInfo::eNo, MemoryRegionInfo::eNo, |
121 | MemoryRegionInfo::eNo, |
122 | MemoryRegionInfo::eYes, |
123 | ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0, |
124 | MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow), |
125 | MemoryRegionInfo( |
126 | make_range(0x7fc094000000, 0x7fc094a00000), |
127 | MemoryRegionInfo::eNo, MemoryRegionInfo::eNo, MemoryRegionInfo::eNo, |
128 | MemoryRegionInfo::eYes, |
129 | MemoryRegionInfo::eYes, |
130 | ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0, |
131 | MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow), |
132 | MemoryRegionInfo( |
133 | make_range(0xffffffffff600000, 0xffffffffff601000), |
134 | MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, MemoryRegionInfo::eYes, |
135 | MemoryRegionInfo::eNo, |
136 | MemoryRegionInfo::eYes, |
137 | ConstString("[vsyscall]" ), MemoryRegionInfo::eDontKnow, 0, |
138 | MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow), |
139 | }, |
140 | "" ))); |
141 | |
142 | class LinuxProcSMapsTestFixture : public LinuxProcMapsTestFixture {}; |
143 | |
144 | INSTANTIATE_TEST_SUITE_P( |
145 | ProcSMapTests, LinuxProcSMapsTestFixture, |
146 | ::testing::Values( |
147 | // Nothing in nothing out |
148 | std::make_tuple("" , MemoryRegionInfos{}, "" ), |
149 | // Uses the same parsing for first line, so same errors but referring to |
150 | // smaps |
151 | std::make_tuple("0/0 rw-p 00000000 00:00 0" , MemoryRegionInfos{}, |
152 | "malformed /proc/{pid}/smaps entry, missing dash " |
153 | "between address range" ), |
154 | // Stop parsing at first error |
155 | std::make_tuple( |
156 | "1111-2222 rw-p 00000000 00:00 0 [foo]\n" |
157 | "0/0 rw-p 00000000 00:00 0" , |
158 | MemoryRegionInfos{ |
159 | MemoryRegionInfo( |
160 | make_range(0x1111, 0x2222), |
161 | MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
162 | MemoryRegionInfo::eNo, |
163 | MemoryRegionInfo::eYes, ConstString("[foo]" ), |
164 | MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow, |
165 | MemoryRegionInfo::eDontKnow), |
166 | }, |
167 | "malformed /proc/{pid}/smaps entry, missing dash between address " |
168 | "range" ), |
169 | // Property line without a region is an error |
170 | std::make_tuple("Referenced: 2188 kB\n" |
171 | "1111-2222 rw-p 00000000 00:00 0 [foo]\n" |
172 | "3333-4444 rw-p 00000000 00:00 0 [bar]\n" , |
173 | MemoryRegionInfos{}, |
174 | "Found a property line without a corresponding mapping " |
175 | "in /proc/{pid}/smaps" ), |
176 | // Single region parses, has no flags |
177 | std::make_tuple( |
178 | "1111-2222 rw-p 00000000 00:00 0 [foo]" , |
179 | MemoryRegionInfos{ |
180 | MemoryRegionInfo( |
181 | make_range(0x1111, 0x2222), |
182 | MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
183 | MemoryRegionInfo::eNo, |
184 | MemoryRegionInfo::eYes, ConstString("[foo]" ), |
185 | MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow, |
186 | MemoryRegionInfo::eDontKnow), |
187 | }, |
188 | "" ), |
189 | // Single shared region parses, has no flags |
190 | std::make_tuple( |
191 | "1111-2222 rw-s 00000000 00:00 0 [foo]" , |
192 | MemoryRegionInfos{ |
193 | MemoryRegionInfo( |
194 | make_range(0x1111, 0x2222), |
195 | MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
196 | MemoryRegionInfo::eYes, |
197 | MemoryRegionInfo::eYes, ConstString("[foo]" ), |
198 | MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow, |
199 | MemoryRegionInfo::eDontKnow), |
200 | }, |
201 | "" ), |
202 | // Single region with flags, other lines ignored |
203 | std::make_tuple( |
204 | "1111-2222 rw-p 00000000 00:00 0 [foo]\n" |
205 | "Referenced: 2188 kB\n" |
206 | "AnonHugePages: 0 kB\n" |
207 | "VmFlags: mt" , |
208 | MemoryRegionInfos{ |
209 | MemoryRegionInfo( |
210 | make_range(0x1111, 0x2222), |
211 | MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
212 | MemoryRegionInfo::eNo, |
213 | MemoryRegionInfo::eYes, ConstString("[foo]" ), |
214 | MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eYes, |
215 | MemoryRegionInfo::eDontKnow), |
216 | }, |
217 | "" ), |
218 | // Whitespace ignored |
219 | std::make_tuple( |
220 | "0-0 rw-p 00000000 00:00 0\n" |
221 | "VmFlags: mt " , |
222 | MemoryRegionInfos{ |
223 | MemoryRegionInfo(make_range(0, 0), |
224 | MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
225 | MemoryRegionInfo::eNo, |
226 | MemoryRegionInfo::eYes, ConstString(nullptr), |
227 | MemoryRegionInfo::eDontKnow, 0, |
228 | MemoryRegionInfo::eYes, |
229 | MemoryRegionInfo::eDontKnow), |
230 | }, |
231 | "" ), |
232 | // VmFlags line means it has flag info, but nothing is set |
233 | std::make_tuple( |
234 | "0-0 rw-p 00000000 00:00 0\n" |
235 | "VmFlags: " , |
236 | MemoryRegionInfos{ |
237 | MemoryRegionInfo(make_range(0, 0), |
238 | MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
239 | MemoryRegionInfo::eNo, |
240 | MemoryRegionInfo::eYes, ConstString(nullptr), |
241 | MemoryRegionInfo::eDontKnow, 0, |
242 | MemoryRegionInfo::eNo, |
243 | MemoryRegionInfo::eDontKnow), |
244 | }, |
245 | "" ), |
246 | // Handle some pages not having a flags line |
247 | std::make_tuple( |
248 | "1111-2222 rw-p 00000000 00:00 0 [foo]\n" |
249 | "Referenced: 2188 kB\n" |
250 | "AnonHugePages: 0 kB\n" |
251 | "3333-4444 r-xp 00000000 00:00 0 [bar]\n" |
252 | "VmFlags: mt" , |
253 | MemoryRegionInfos{ |
254 | MemoryRegionInfo( |
255 | make_range(0x1111, 0x2222), |
256 | MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
257 | MemoryRegionInfo::eNo, |
258 | MemoryRegionInfo::eYes, ConstString("[foo]" ), |
259 | MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow, |
260 | MemoryRegionInfo::eDontKnow), |
261 | MemoryRegionInfo( |
262 | make_range(0x3333, 0x4444), MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, MemoryRegionInfo::eYes, |
263 | MemoryRegionInfo::eNo, |
264 | MemoryRegionInfo::eYes, ConstString("[bar]" ), |
265 | MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eYes, |
266 | MemoryRegionInfo::eDontKnow), |
267 | }, |
268 | "" ), |
269 | // Handle no pages having a flags line (older kernels) |
270 | std::make_tuple( |
271 | "1111-2222 rw-p 00000000 00:00 0\n" |
272 | "Referenced: 2188 kB\n" |
273 | "AnonHugePages: 0 kB\n" |
274 | "3333-4444 r-xp 00000000 00:00 0\n" |
275 | "KernelPageSize: 4 kB\n" |
276 | "MMUPageSize: 4 kB\n" , |
277 | MemoryRegionInfos{ |
278 | MemoryRegionInfo( |
279 | make_range(0x1111, 0x2222), |
280 | MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, |
281 | MemoryRegionInfo::eNo, |
282 | MemoryRegionInfo::eYes, ConstString(nullptr), |
283 | MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow, |
284 | MemoryRegionInfo::eDontKnow), |
285 | MemoryRegionInfo( |
286 | make_range(0x3333, 0x4444), |
287 | MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, MemoryRegionInfo::eYes, |
288 | MemoryRegionInfo::eNo, |
289 | MemoryRegionInfo::eYes, ConstString(nullptr), |
290 | MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow, |
291 | MemoryRegionInfo::eDontKnow), |
292 | }, |
293 | "" ))); |
294 | |
295 | TEST_P(LinuxProcSMapsTestFixture, ParseSMapRegions) { |
296 | auto params = GetParam(); |
297 | ParseLinuxSMapRegions(linux_smap: std::get<0>(t&: params), callback); |
298 | check_regions(params); |
299 | } |
300 | |