Skip to content

Commit 99afd7d

Browse files
Thiabaud Engelbrechtchromeos-ci-prod
authored andcommitted
[base/debug] Add function to read smaps_rollup
This CL adds a utility function for reading /proc/self/smaps_rollup, which will be used to measure the impact of compacting process memory on Android. Bug: 375387998 Change-Id: Id0f4852e86759411d37678f84e64d8f6b8a514e1 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5960816 Commit-Queue: Thiabaud Engelbrecht <thiabaud@google.com> Reviewed-by: Francois Pierre Doray <fdoray@chromium.org> Reviewed-by: Benoit Lize <lizeb@chromium.org> Cr-Commit-Position: refs/heads/main@{#1375245} CrOS-Libchrome-Original-Commit: 309fb1eaad193ff82fbc3f00e158ea65e7332cdc
1 parent fcea651 commit 99afd7d

File tree

3 files changed

+182
-8
lines changed

3 files changed

+182
-8
lines changed

base/debug/proc_maps_linux.cc

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
#include "base/files/file_util.h"
1616
#include "base/files/scoped_file.h"
17+
#include "base/format_macros.h"
1718
#include "base/logging.h"
19+
#include "base/memory/page_size.h"
1820
#include "base/strings/string_split.h"
1921
#include "build/build_config.h"
2022

@@ -170,5 +172,72 @@ bool ParseProcMaps(const std::string& input,
170172
return true;
171173
}
172174

175+
std::optional<SmapsRollup> ParseSmapsRollup(const std::string& buffer) {
176+
std::vector<std::string> lines =
177+
SplitString(buffer, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
178+
179+
std::unordered_map<std::string, size_t> tmp;
180+
for (const auto& line : lines) {
181+
// This should be more than enough space for any output we get (but we also
182+
// verify the size below).
183+
std::string key;
184+
key.resize(100);
185+
size_t val;
186+
if (sscanf(line.c_str(), "%99s %" PRIuS " kB", key.data(), &val) == 2) {
187+
// sscanf writes a nul-byte at the end of the result, so |strlen| is safe
188+
// here. |resize| does not count the length of the nul-byte, and we want
189+
// to trim off the trailing colon at the end, so we use |strlen - 1| here.
190+
key.resize(strlen(key.c_str()) - 1);
191+
tmp[key] = val * 1024;
192+
}
193+
}
194+
195+
SmapsRollup smaps_rollup;
196+
197+
smaps_rollup.rss = tmp["Rss"];
198+
smaps_rollup.pss = tmp["Pss"];
199+
smaps_rollup.pss_anon = tmp["Pss_Anon"];
200+
smaps_rollup.pss_file = tmp["Pss_File"];
201+
smaps_rollup.pss_shmem = tmp["Pss_Shmem"];
202+
smaps_rollup.private_dirty = tmp["Private_Dirty"];
203+
smaps_rollup.swap = tmp["Swap"];
204+
smaps_rollup.swap_pss = tmp["SwapPss"];
205+
206+
return smaps_rollup;
207+
}
208+
209+
std::optional<SmapsRollup> ReadAndParseSmapsRollup() {
210+
const size_t read_size = base::GetPageSize();
211+
212+
base::ScopedFD fd(HANDLE_EINTR(open("/proc/self/smaps_rollup", O_RDONLY)));
213+
if (!fd.is_valid()) {
214+
DPLOG(ERROR) << "Couldn't open /proc/self/smaps_rollup";
215+
return std::nullopt;
216+
}
217+
218+
std::string buffer;
219+
buffer.resize(read_size);
220+
221+
ssize_t bytes_read = HANDLE_EINTR(
222+
read(fd.get(), static_cast<void*>(buffer.data()), read_size));
223+
if (bytes_read < 0) {
224+
DPLOG(ERROR) << "Couldn't read /proc/self/smaps_rollup";
225+
return std::nullopt;
226+
}
227+
228+
// We expect to read a few hundred bytes, which should be significantly less
229+
// the page size.
230+
DCHECK(static_cast<size_t>(bytes_read) < read_size);
231+
232+
buffer.resize(static_cast<size_t>(bytes_read));
233+
234+
return ParseSmapsRollup(buffer);
235+
}
236+
237+
std::optional<SmapsRollup> ParseSmapsRollupForTesting(
238+
const std::string& smaps_rollup) {
239+
return ParseSmapsRollup(smaps_rollup);
240+
}
241+
173242
} // namespace debug
174243
} // namespace base

base/debug/proc_maps_linux.h

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77

88
#include <stdint.h>
99

10+
#include <optional>
1011
#include <string>
1112
#include <vector>
1213

1314
#include "base/base_export.h"
1415

15-
namespace base {
16-
namespace debug {
16+
namespace base::debug {
1717

1818
// Describes a region of mapped memory and the path of the file mapped.
1919
struct BASE_EXPORT MappedMemoryRegion {
@@ -96,7 +96,24 @@ BASE_EXPORT bool ReadProcMaps(std::string* proc_maps);
9696
BASE_EXPORT bool ParseProcMaps(const std::string& input,
9797
std::vector<MappedMemoryRegion>* regions);
9898

99-
} // namespace debug
100-
} // namespace base
99+
struct SmapsRollup {
100+
size_t rss = 0;
101+
size_t pss = 0;
102+
size_t pss_anon = 0;
103+
size_t pss_file = 0;
104+
size_t pss_shmem = 0;
105+
size_t private_dirty = 0;
106+
size_t swap = 0;
107+
size_t swap_pss = 0;
108+
};
109+
110+
// Attempts to read /proc/self/smaps_rollup. Returns nullopt on error.
111+
BASE_EXPORT std::optional<SmapsRollup> ReadAndParseSmapsRollup();
112+
113+
// |smaps_rollup| should be the result of reading /proc/*/smaps_rollup.
114+
BASE_EXPORT std::optional<SmapsRollup> ParseSmapsRollupForTesting(
115+
const std::string& smaps_rollup);
116+
117+
} // namespace base::debug
101118

102119
#endif // BASE_DEBUG_PROC_MAPS_LINUX_H_

base/debug/proc_maps_linux_unittest.cc

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,44 @@
1111

1212
#include <stddef.h>
1313
#include <stdint.h>
14+
#include <sys/utsname.h>
1415

1516
#include "base/files/file_path.h"
1617
#include "base/path_service.h"
1718
#include "base/strings/stringprintf.h"
19+
#include "base/system/sys_info.h"
1820
#include "base/threading/platform_thread.h"
1921
#include "build/build_config.h"
2022
#include "testing/gtest/include/gtest/gtest.h"
2123

22-
namespace base {
23-
namespace debug {
24+
namespace base::debug {
25+
26+
namespace {
27+
28+
// SmapsRollup was added in Linux 4.14.
29+
bool IsSmapsRollupSupported() {
30+
struct utsname info;
31+
if (uname(&info) < 0) {
32+
NOTREACHED();
33+
}
34+
35+
int major, minor, patch;
36+
if (sscanf(info.release, "%d.%d.%d", &major, &minor, &patch) < 3) {
37+
NOTREACHED();
38+
}
39+
40+
if (major > 4) {
41+
return true;
42+
}
43+
44+
if (major < 4 || minor < 14) {
45+
return false;
46+
}
47+
48+
return true;
49+
}
50+
51+
} // namespace
2452

2553
TEST(ProcMapsTest, Empty) {
2654
std::vector<MappedMemoryRegion> regions;
@@ -330,5 +358,65 @@ TEST(ProcMapsTest, ParseProcMapsWeirdCorrectInput) {
330358
EXPECT_EQ("[vsys call]", regions[4].path);
331359
}
332360

333-
} // namespace debug
334-
} // namespace base
361+
TEST(SmapsRollupTest, ReadAndParse) {
362+
if (!IsSmapsRollupSupported()) {
363+
GTEST_SKIP() << "smaps_rollup not supported";
364+
}
365+
366+
const auto result = ReadAndParseSmapsRollup();
367+
368+
EXPECT_TRUE(result.has_value());
369+
370+
SmapsRollup smaps_rollup = result.value();
371+
372+
EXPECT_GT(smaps_rollup.rss, 0u);
373+
EXPECT_GT(smaps_rollup.pss, 0u);
374+
EXPECT_GT(smaps_rollup.pss_anon, 0u);
375+
EXPECT_GT(smaps_rollup.private_dirty, 0u);
376+
}
377+
378+
TEST(SmapsRollupTest, Valid) {
379+
const auto result = ParseSmapsRollupForTesting(
380+
// This input is based on a real one captured locally, but with some
381+
// values changed in order to make them unique (to test that the correct
382+
// values are being parsed).
383+
R"(55f4d118e000-7ffff6e62000 ---p 00000000 00:00 0 [rollup]
384+
Rss: 1908 kB
385+
Pss: 573 kB
386+
Pss_Dirty: 144 kB
387+
Pss_Anon: 100 kB
388+
Pss_File: 469 kB
389+
Pss_Shmem: 12 kB
390+
Shared_Clean: 1356 kB
391+
Shared_Dirty: 0 kB
392+
Private_Clean: 448 kB
393+
Private_Dirty: 104 kB
394+
Referenced: 1900 kB
395+
Anonymous: 105 kB
396+
KSM: 0 kB
397+
LazyFree: 0 kB
398+
AnonHugePages: 0 kB
399+
ShmemPmdMapped: 0 kB
400+
FilePmdMapped: 0 kB
401+
Shared_Hugetlb: 0 kB
402+
Private_Hugetlb: 0 kB
403+
Swap: 10 kB
404+
SwapPss: 20 kB
405+
Locked: 0 kB
406+
)");
407+
408+
EXPECT_TRUE(result.has_value());
409+
410+
SmapsRollup smaps_rollup = result.value();
411+
412+
EXPECT_EQ(smaps_rollup.rss, 1024 * 1908u);
413+
EXPECT_EQ(smaps_rollup.pss, 1024 * 573u);
414+
EXPECT_EQ(smaps_rollup.private_dirty, 1024 * 104u);
415+
EXPECT_EQ(smaps_rollup.pss_anon, 1024 * 100u);
416+
EXPECT_EQ(smaps_rollup.pss_file, 1024 * 469u);
417+
EXPECT_EQ(smaps_rollup.pss_shmem, 1024 * 12u);
418+
EXPECT_EQ(smaps_rollup.swap, 1024 * 10u);
419+
EXPECT_EQ(smaps_rollup.swap_pss, 1024 * 20u);
420+
}
421+
422+
} // namespace base::debug

0 commit comments

Comments
 (0)