Skip to content

Commit 7ccfb66

Browse files
tstuefepull[bot]
authored andcommitted
8322475: Extend printing for System.map
Reviewed-by: sgehwolf, jsjolen
1 parent 048069a commit 7ccfb66

14 files changed

+513
-199
lines changed
+144-39
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
2-
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3-
* Copyright (c) 2023, Red Hat, Inc. and/or its affiliates.
2+
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2023, 2024, Red Hat, Inc. and/or its affiliates.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
66
* This code is free software; you can redistribute it and/or modify it
@@ -25,60 +25,165 @@
2525

2626
#include "precompiled.hpp"
2727

28-
#include "runtime/os.hpp"
2928
#include "nmt/memMapPrinter.hpp"
29+
#include "procMapsParser.hpp"
30+
#include "runtime/os.hpp"
31+
#include "utilities/align.hpp"
3032
#include "utilities/globalDefinitions.hpp"
33+
#include "utilities/powerOfTwo.hpp"
34+
3135
#include <limits.h>
3236

33-
struct ProcMapsInfo {
34-
void* from = 0;
35-
void* to = 0;
36-
char prot[20 + 1];
37-
char offset[20 + 1];
38-
char dev[20 + 1];
39-
char inode[20 + 1];
40-
char filename[1024 + 1];
41-
42-
bool scan_proc_maps_line(const char* line) {
43-
prot[0] = offset[0] = dev[0] = inode[0] = filename[0] = '\0';
44-
const int items_read = ::sscanf(line, "%p-%p %20s %20s %20s %20s %1024s",
45-
&from, &to, prot, offset, dev, inode, filename);
46-
return items_read >= 2; // need at least from and to
37+
class ProcSmapsSummary {
38+
unsigned _num_mappings;
39+
size_t _vsize; // combined virtual size
40+
size_t _rss; // combined resident set size
41+
size_t _committed; // combined committed size
42+
size_t _shared; // combined shared size
43+
size_t _swapped_out; // combined amount of swapped-out memory
44+
size_t _hugetlb; // combined amount of memory backed by explicit huge pages
45+
size_t _thp; // combined amount of memory backed by THPs
46+
public:
47+
ProcSmapsSummary() : _num_mappings(0), _vsize(0), _rss(0), _committed(0), _shared(0),
48+
_swapped_out(0), _hugetlb(0), _thp(0) {}
49+
void add_mapping(const ProcSmapsInfo& info) {
50+
_num_mappings++;
51+
_vsize += info.vsize();
52+
_rss += info.rss;
53+
_committed += info.nr ? 0 : info.vsize();
54+
_shared += info.sh ? info.vsize() : 0;
55+
_swapped_out += info.swap;
56+
_hugetlb += info.private_hugetlb + info.shared_hugetlb;
57+
_thp += info.anonhugepages;
58+
}
59+
60+
void print_on(const MappingPrintSession& session) const {
61+
outputStream* st = session.out();
62+
st->print_cr("Number of mappings: %u", _num_mappings);
63+
st->print_cr(" vsize: %zu (" PROPERFMT ")", _vsize, PROPERFMTARGS(_vsize));
64+
st->print_cr(" rss: %zu (" PROPERFMT ")", _rss, PROPERFMTARGS(_rss));
65+
st->print_cr(" committed: %zu (" PROPERFMT ")", _committed, PROPERFMTARGS(_committed));
66+
st->print_cr(" shared: %zu (" PROPERFMT ")", _shared, PROPERFMTARGS(_shared));
67+
st->print_cr(" swapped out: %zu (" PROPERFMT ")", _swapped_out, PROPERFMTARGS(_swapped_out));
68+
st->print_cr(" using thp: %zu (" PROPERFMT ")", _thp, PROPERFMTARGS(_thp));
69+
st->print_cr(" hugetlb: %zu (" PROPERFMT ")", _hugetlb, PROPERFMTARGS(_hugetlb));
4770
}
4871
};
4972

50-
class LinuxMappingPrintInformation : public MappingPrintInformation {
51-
const ProcMapsInfo _info;
73+
class ProcSmapsPrinter {
74+
const MappingPrintSession& _session;
5275
public:
76+
ProcSmapsPrinter(const MappingPrintSession& session) :
77+
_session(session)
78+
{}
5379

54-
LinuxMappingPrintInformation(const void* from, const void* to, const ProcMapsInfo* info) :
55-
MappingPrintInformation(from, to), _info(*info) {}
80+
void print_single_mapping(const ProcSmapsInfo& info) const {
81+
outputStream* st = _session.out();
82+
#define INDENT_BY(n) \
83+
if (st->fill_to(n) == 0) { \
84+
st->print(" "); \
85+
}
86+
st->print(PTR_FORMAT "-" PTR_FORMAT, p2i(info.from), p2i(info.to));
87+
INDENT_BY(38);
88+
st->print("%12zu", info.vsize());
89+
INDENT_BY(51);
90+
st->print("%s", info.prot);
91+
INDENT_BY(56);
92+
st->print("%12zu", info.rss);
93+
INDENT_BY(69);
94+
st->print("%12zu", info.private_hugetlb);
95+
INDENT_BY(82);
96+
st->print(EXACTFMT, EXACTFMTARGS(info.kernelpagesize));
97+
{
98+
INDENT_BY(87);
99+
int num_printed = 0;
100+
#define PRINTIF(cond, s) \
101+
if (cond) { \
102+
st->print("%s%s", (num_printed > 0 ? "," : ""), s); \
103+
num_printed++; \
104+
}
105+
PRINTIF(info.sh, "shrd");
106+
PRINTIF(!info.nr, "com");
107+
PRINTIF(info.swap > 0, "swap");
108+
PRINTIF(info.ht, "huge");
109+
PRINTIF(info.anonhugepages > 0, "thp");
110+
PRINTIF(info.hg, "thpad");
111+
PRINTIF(info.nh, "nothp");
112+
if (num_printed == 0) {
113+
st->print("-");
114+
}
115+
#undef PRINTIF
116+
}
117+
INDENT_BY(104);
118+
if (!_session.print_nmt_info_for_region(info.from, info.to)) {
119+
st->print("-");
120+
}
121+
INDENT_BY(142);
122+
st->print_raw(info.filename[0] == '\0' ? "-" : info.filename);
123+
#undef INDENT_BY
124+
st->cr();
125+
}
56126

57-
void print_OS_specific_details(outputStream* st) const override {
58-
st->print("%s %s ", _info.prot, _info.offset);
127+
void print_legend() const {
128+
outputStream* st = _session.out();
129+
st->print_cr("from, to, vsize: address range and size");
130+
st->print_cr("prot: protection");
131+
st->print_cr("rss: resident set size");
132+
st->print_cr("hugetlb: size of private hugetlb pages");
133+
st->print_cr("pgsz: page size");
134+
st->print_cr("notes: mapping information (detail mode only)");
135+
st->print_cr(" shrd: mapping is shared");
136+
st->print_cr(" com: mapping committed (swap space reserved)");
137+
st->print_cr(" swap: mapping partly or completely swapped out");
138+
st->print_cr(" thp: mapping uses THP");
139+
st->print_cr(" thpad: mapping is THP-madvised");
140+
st->print_cr(" nothp: mapping is forbidden to use THP");
141+
st->print_cr(" huge: mapping uses hugetlb pages");
142+
st->print_cr("vm info: VM information (requires NMT)");
143+
{
144+
streamIndentor si(st, 16);
145+
_session.print_nmt_flag_legend();
146+
}
147+
st->print_cr("file: file mapped, if mapping is not anonymous");
59148
}
60149

61-
const char* filename() const override { return _info.filename; }
150+
void print_header() const {
151+
outputStream* st = _session.out();
152+
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
153+
// 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
154+
// 0x0000000414000000-0x0000000453000000 123456789012 rw-p 123456789012 123456789012 16g thp,thpadv STACK-340754-Monitor-Deflation-Thread /shared/tmp.txt
155+
st->print_cr("from to vsize prot rss hugetlb pgsz notes info file");
156+
st->print_cr("========================================================================================================================================================================");
157+
}
62158
};
63159

64-
void MemMapPrinter::pd_print_header(outputStream* st) {
65-
st->print_cr("size prot offset What");
66-
}
67-
68-
void MemMapPrinter::pd_iterate_all_mappings(MappingPrintClosure& closure) {
69-
FILE* f = os::fopen("/proc/self/maps", "r");
160+
void MemMapPrinter::pd_print_all_mappings(const MappingPrintSession& session) {
161+
constexpr char filename[] = "/proc/self/smaps";
162+
FILE* f = os::fopen(filename, "r");
70163
if (f == nullptr) {
164+
session.out()->print_cr("Cannot open %s", filename);
71165
return;
72166
}
73-
constexpr size_t linesize = sizeof(ProcMapsInfo);
74-
char line[linesize];
75-
while (fgets(line, sizeof(line), f) == line) {
76-
line[sizeof(line) - 1] = '\0';
77-
ProcMapsInfo info;
78-
if (info.scan_proc_maps_line(line)) {
79-
LinuxMappingPrintInformation mapinfo(info.from, info.to, &info);
80-
closure.do_it(&mapinfo);
81-
}
167+
168+
ProcSmapsPrinter printer(session);
169+
ProcSmapsSummary summary;
170+
171+
outputStream* const st = session.out();
172+
173+
printer.print_legend();
174+
st->cr();
175+
printer.print_header();
176+
177+
ProcSmapsInfo info;
178+
ProcSmapsParser parser(f);
179+
while (parser.parse_next(info)) {
180+
printer.print_single_mapping(info);
181+
summary.add_mapping(info);
82182
}
183+
st->cr();
184+
185+
summary.print_on(session);
186+
st->cr();
187+
83188
::fclose(f);
84189
}
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright (c) 2024, Red Hat, Inc. and/or its affiliates.
3+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* This code is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* version 2 for more details (a copy is included in the LICENSE file that
14+
* accompanied this code).
15+
*
16+
* You should have received a copy of the GNU General Public License version
17+
* 2 along with this work; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*
20+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21+
* or visit www.oracle.com if you need additional information or have any
22+
* questions.
23+
*
24+
*/
25+
26+
#include "precompiled.hpp"
27+
28+
#include "procMapsParser.hpp"
29+
#include "runtime/os.hpp"
30+
#include "utilities/globalDefinitions.hpp"
31+
32+
static bool is_lowercase_hex(char c) {
33+
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
34+
}
35+
36+
static size_t max_mapping_line_len() {
37+
return 100 + // everything but the file name
38+
os::vm_page_size() // the file name (kernel limits /proc/pid/cmdline to one page
39+
;
40+
}
41+
42+
ProcSmapsParser::ProcSmapsParser(FILE* f) :
43+
_f(f), _linelen(max_mapping_line_len()), _line(nullptr) {
44+
assert(_f != nullptr, "Invalid file handle given");
45+
_line = NEW_C_HEAP_ARRAY(char, max_mapping_line_len(), mtInternal);
46+
_line[0] = '\0';
47+
}
48+
49+
ProcSmapsParser::~ProcSmapsParser() {
50+
FREE_C_HEAP_ARRAY(char, _line);
51+
}
52+
53+
bool ProcSmapsParser::read_line() {
54+
_line[0] = '\0';
55+
return ::fgets(_line, _linelen, _f) != nullptr;
56+
}
57+
58+
bool ProcSmapsParser::is_header_line() {
59+
// e.g. ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
60+
return is_lowercase_hex(_line[0]); // All non-header lines in /proc/pid/smaps start with upper-case letters.
61+
}
62+
63+
void ProcSmapsParser::scan_header_line(ProcSmapsInfo& out) {
64+
const int items_read = ::sscanf(_line, "%p-%p %20s %*s %*s %*s %1024s",
65+
&out.from, &out.to, out.prot, out.filename);
66+
assert(items_read >= 2, "Expected header_line");
67+
}
68+
69+
void ProcSmapsParser::scan_additional_line(ProcSmapsInfo& out) {
70+
#define SCAN(key, var) \
71+
if (::sscanf(_line, key ": %zu kB", &var) == 1) { \
72+
var *= K; \
73+
return; \
74+
}
75+
SCAN("KernelPageSize", out.kernelpagesize);
76+
SCAN("Rss", out.rss);
77+
SCAN("AnonHugePages", out.anonhugepages);
78+
SCAN("Private_Hugetlb", out.private_hugetlb);
79+
SCAN("Shared_Hugetlb", out.shared_hugetlb);
80+
SCAN("Swap", out.swap);
81+
int i = 0;
82+
#undef SCAN
83+
// scan some flags too
84+
if (strncmp(_line, "VmFlags:", 8) == 0) {
85+
#define SCAN(flag) { out.flag = (::strstr(_line + 8, " " #flag) != nullptr); }
86+
SCAN(rd);
87+
SCAN(wr);
88+
SCAN(ex);
89+
SCAN(nr);
90+
SCAN(sh);
91+
SCAN(hg);
92+
SCAN(ht);
93+
SCAN(nh);
94+
#undef SCAN
95+
}
96+
}
97+
98+
// Starts or continues parsing. Returns true on success,
99+
// false on EOF or on error.
100+
bool ProcSmapsParser::parse_next(ProcSmapsInfo& out) {
101+
102+
// Information about a single mapping reaches across several lines.
103+
out.reset();
104+
105+
// Read header line, unless we already read it
106+
if (_line[0] == '\0') {
107+
if (!read_line()) {
108+
return false;
109+
}
110+
}
111+
assert(is_header_line(), "Not a header line: \"%s\".", _line);
112+
scan_header_line(out);
113+
114+
// Now read until we encounter the next header line or EOF or an error.
115+
bool ok = false, stop = false;
116+
do {
117+
ok = read_line();
118+
stop = !ok || is_header_line();
119+
if (!stop) {
120+
scan_additional_line(out);
121+
}
122+
} while (!stop);
123+
124+
return ok;
125+
}

0 commit comments

Comments
 (0)