8322475: Extend printing for System.map
Reviewed-by: sgehwolf, jsjolen
This commit is contained in:
parent
19a8a2baa9
commit
8aaec37ace
src/hotspot
os/linux
share
test/hotspot/jtreg/serviceability/dcmd/vm
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, Red Hat, Inc. and/or its affiliates.
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2024, Red Hat, Inc. and/or its affiliates.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,60 +25,165 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
|
||||
#include "runtime/os.hpp"
|
||||
#include "nmt/memMapPrinter.hpp"
|
||||
#include "procMapsParser.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/powerOfTwo.hpp"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
struct ProcMapsInfo {
|
||||
void* from = 0;
|
||||
void* to = 0;
|
||||
char prot[20 + 1];
|
||||
char offset[20 + 1];
|
||||
char dev[20 + 1];
|
||||
char inode[20 + 1];
|
||||
char filename[1024 + 1];
|
||||
|
||||
bool scan_proc_maps_line(const char* line) {
|
||||
prot[0] = offset[0] = dev[0] = inode[0] = filename[0] = '\0';
|
||||
const int items_read = ::sscanf(line, "%p-%p %20s %20s %20s %20s %1024s",
|
||||
&from, &to, prot, offset, dev, inode, filename);
|
||||
return items_read >= 2; // need at least from and to
|
||||
}
|
||||
};
|
||||
|
||||
class LinuxMappingPrintInformation : public MappingPrintInformation {
|
||||
const ProcMapsInfo _info;
|
||||
class ProcSmapsSummary {
|
||||
unsigned _num_mappings;
|
||||
size_t _vsize; // combined virtual size
|
||||
size_t _rss; // combined resident set size
|
||||
size_t _committed; // combined committed size
|
||||
size_t _shared; // combined shared size
|
||||
size_t _swapped_out; // combined amount of swapped-out memory
|
||||
size_t _hugetlb; // combined amount of memory backed by explicit huge pages
|
||||
size_t _thp; // combined amount of memory backed by THPs
|
||||
public:
|
||||
|
||||
LinuxMappingPrintInformation(const void* from, const void* to, const ProcMapsInfo* info) :
|
||||
MappingPrintInformation(from, to), _info(*info) {}
|
||||
|
||||
void print_OS_specific_details(outputStream* st) const override {
|
||||
st->print("%s %s ", _info.prot, _info.offset);
|
||||
ProcSmapsSummary() : _num_mappings(0), _vsize(0), _rss(0), _committed(0), _shared(0),
|
||||
_swapped_out(0), _hugetlb(0), _thp(0) {}
|
||||
void add_mapping(const ProcSmapsInfo& info) {
|
||||
_num_mappings++;
|
||||
_vsize += info.vsize();
|
||||
_rss += info.rss;
|
||||
_committed += info.nr ? 0 : info.vsize();
|
||||
_shared += info.sh ? info.vsize() : 0;
|
||||
_swapped_out += info.swap;
|
||||
_hugetlb += info.private_hugetlb + info.shared_hugetlb;
|
||||
_thp += info.anonhugepages;
|
||||
}
|
||||
|
||||
const char* filename() const override { return _info.filename; }
|
||||
void print_on(const MappingPrintSession& session) const {
|
||||
outputStream* st = session.out();
|
||||
st->print_cr("Number of mappings: %u", _num_mappings);
|
||||
st->print_cr(" vsize: %zu (" PROPERFMT ")", _vsize, PROPERFMTARGS(_vsize));
|
||||
st->print_cr(" rss: %zu (" PROPERFMT ")", _rss, PROPERFMTARGS(_rss));
|
||||
st->print_cr(" committed: %zu (" PROPERFMT ")", _committed, PROPERFMTARGS(_committed));
|
||||
st->print_cr(" shared: %zu (" PROPERFMT ")", _shared, PROPERFMTARGS(_shared));
|
||||
st->print_cr(" swapped out: %zu (" PROPERFMT ")", _swapped_out, PROPERFMTARGS(_swapped_out));
|
||||
st->print_cr(" using thp: %zu (" PROPERFMT ")", _thp, PROPERFMTARGS(_thp));
|
||||
st->print_cr(" hugetlb: %zu (" PROPERFMT ")", _hugetlb, PROPERFMTARGS(_hugetlb));
|
||||
}
|
||||
};
|
||||
|
||||
void MemMapPrinter::pd_print_header(outputStream* st) {
|
||||
st->print_cr("size prot offset What");
|
||||
}
|
||||
class ProcSmapsPrinter {
|
||||
const MappingPrintSession& _session;
|
||||
public:
|
||||
ProcSmapsPrinter(const MappingPrintSession& session) :
|
||||
_session(session)
|
||||
{}
|
||||
|
||||
void MemMapPrinter::pd_iterate_all_mappings(MappingPrintClosure& closure) {
|
||||
FILE* f = os::fopen("/proc/self/maps", "r");
|
||||
void print_single_mapping(const ProcSmapsInfo& info) const {
|
||||
outputStream* st = _session.out();
|
||||
#define INDENT_BY(n) \
|
||||
if (st->fill_to(n) == 0) { \
|
||||
st->print(" "); \
|
||||
}
|
||||
st->print(PTR_FORMAT "-" PTR_FORMAT, p2i(info.from), p2i(info.to));
|
||||
INDENT_BY(38);
|
||||
st->print("%12zu", info.vsize());
|
||||
INDENT_BY(51);
|
||||
st->print("%s", info.prot);
|
||||
INDENT_BY(56);
|
||||
st->print("%12zu", info.rss);
|
||||
INDENT_BY(69);
|
||||
st->print("%12zu", info.private_hugetlb);
|
||||
INDENT_BY(82);
|
||||
st->print(EXACTFMT, EXACTFMTARGS(info.kernelpagesize));
|
||||
{
|
||||
INDENT_BY(87);
|
||||
int num_printed = 0;
|
||||
#define PRINTIF(cond, s) \
|
||||
if (cond) { \
|
||||
st->print("%s%s", (num_printed > 0 ? "," : ""), s); \
|
||||
num_printed++; \
|
||||
}
|
||||
PRINTIF(info.sh, "shrd");
|
||||
PRINTIF(!info.nr, "com");
|
||||
PRINTIF(info.swap > 0, "swap");
|
||||
PRINTIF(info.ht, "huge");
|
||||
PRINTIF(info.anonhugepages > 0, "thp");
|
||||
PRINTIF(info.hg, "thpad");
|
||||
PRINTIF(info.nh, "nothp");
|
||||
if (num_printed == 0) {
|
||||
st->print("-");
|
||||
}
|
||||
#undef PRINTIF
|
||||
}
|
||||
INDENT_BY(104);
|
||||
if (!_session.print_nmt_info_for_region(info.from, info.to)) {
|
||||
st->print("-");
|
||||
}
|
||||
INDENT_BY(142);
|
||||
st->print_raw(info.filename[0] == '\0' ? "-" : info.filename);
|
||||
#undef INDENT_BY
|
||||
st->cr();
|
||||
}
|
||||
|
||||
void print_legend() const {
|
||||
outputStream* st = _session.out();
|
||||
st->print_cr("from, to, vsize: address range and size");
|
||||
st->print_cr("prot: protection");
|
||||
st->print_cr("rss: resident set size");
|
||||
st->print_cr("hugetlb: size of private hugetlb pages");
|
||||
st->print_cr("pgsz: page size");
|
||||
st->print_cr("notes: mapping information (detail mode only)");
|
||||
st->print_cr(" shrd: mapping is shared");
|
||||
st->print_cr(" com: mapping committed (swap space reserved)");
|
||||
st->print_cr(" swap: mapping partly or completely swapped out");
|
||||
st->print_cr(" thp: mapping uses THP");
|
||||
st->print_cr(" thpad: mapping is THP-madvised");
|
||||
st->print_cr(" nothp: mapping is forbidden to use THP");
|
||||
st->print_cr(" huge: mapping uses hugetlb pages");
|
||||
st->print_cr("vm info: VM information (requires NMT)");
|
||||
{
|
||||
streamIndentor si(st, 16);
|
||||
_session.print_nmt_flag_legend();
|
||||
}
|
||||
st->print_cr("file: file mapped, if mapping is not anonymous");
|
||||
}
|
||||
|
||||
void print_header() const {
|
||||
outputStream* st = _session.out();
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
|
||||
// 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
|
||||
// 0x0000000414000000-0x0000000453000000 123456789012 rw-p 123456789012 123456789012 16g thp,thpadv STACK-340754-Monitor-Deflation-Thread /shared/tmp.txt
|
||||
st->print_cr("from to vsize prot rss hugetlb pgsz notes info file");
|
||||
st->print_cr("========================================================================================================================================================================");
|
||||
}
|
||||
};
|
||||
|
||||
void MemMapPrinter::pd_print_all_mappings(const MappingPrintSession& session) {
|
||||
constexpr char filename[] = "/proc/self/smaps";
|
||||
FILE* f = os::fopen(filename, "r");
|
||||
if (f == nullptr) {
|
||||
session.out()->print_cr("Cannot open %s", filename);
|
||||
return;
|
||||
}
|
||||
constexpr size_t linesize = sizeof(ProcMapsInfo);
|
||||
char line[linesize];
|
||||
while (fgets(line, sizeof(line), f) == line) {
|
||||
line[sizeof(line) - 1] = '\0';
|
||||
ProcMapsInfo info;
|
||||
if (info.scan_proc_maps_line(line)) {
|
||||
LinuxMappingPrintInformation mapinfo(info.from, info.to, &info);
|
||||
closure.do_it(&mapinfo);
|
||||
}
|
||||
|
||||
ProcSmapsPrinter printer(session);
|
||||
ProcSmapsSummary summary;
|
||||
|
||||
outputStream* const st = session.out();
|
||||
|
||||
printer.print_legend();
|
||||
st->cr();
|
||||
printer.print_header();
|
||||
|
||||
ProcSmapsInfo info;
|
||||
ProcSmapsParser parser(f);
|
||||
while (parser.parse_next(info)) {
|
||||
printer.print_single_mapping(info);
|
||||
summary.add_mapping(info);
|
||||
}
|
||||
st->cr();
|
||||
|
||||
summary.print_on(session);
|
||||
st->cr();
|
||||
|
||||
::fclose(f);
|
||||
}
|
||||
|
125
src/hotspot/os/linux/procMapsParser.cpp
Normal file
125
src/hotspot/os/linux/procMapsParser.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Red Hat, Inc. and/or its affiliates.
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
|
||||
#include "procMapsParser.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
static bool is_lowercase_hex(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
|
||||
}
|
||||
|
||||
static size_t max_mapping_line_len() {
|
||||
return 100 + // everything but the file name
|
||||
os::vm_page_size() // the file name (kernel limits /proc/pid/cmdline to one page
|
||||
;
|
||||
}
|
||||
|
||||
ProcSmapsParser::ProcSmapsParser(FILE* f) :
|
||||
_f(f), _linelen(max_mapping_line_len()), _line(nullptr) {
|
||||
assert(_f != nullptr, "Invalid file handle given");
|
||||
_line = NEW_C_HEAP_ARRAY(char, max_mapping_line_len(), mtInternal);
|
||||
_line[0] = '\0';
|
||||
}
|
||||
|
||||
ProcSmapsParser::~ProcSmapsParser() {
|
||||
FREE_C_HEAP_ARRAY(char, _line);
|
||||
}
|
||||
|
||||
bool ProcSmapsParser::read_line() {
|
||||
_line[0] = '\0';
|
||||
return ::fgets(_line, _linelen, _f) != nullptr;
|
||||
}
|
||||
|
||||
bool ProcSmapsParser::is_header_line() {
|
||||
// e.g. ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
|
||||
return is_lowercase_hex(_line[0]); // All non-header lines in /proc/pid/smaps start with upper-case letters.
|
||||
}
|
||||
|
||||
void ProcSmapsParser::scan_header_line(ProcSmapsInfo& out) {
|
||||
const int items_read = ::sscanf(_line, "%p-%p %20s %*s %*s %*s %1024s",
|
||||
&out.from, &out.to, out.prot, out.filename);
|
||||
assert(items_read >= 2, "Expected header_line");
|
||||
}
|
||||
|
||||
void ProcSmapsParser::scan_additional_line(ProcSmapsInfo& out) {
|
||||
#define SCAN(key, var) \
|
||||
if (::sscanf(_line, key ": %zu kB", &var) == 1) { \
|
||||
var *= K; \
|
||||
return; \
|
||||
}
|
||||
SCAN("KernelPageSize", out.kernelpagesize);
|
||||
SCAN("Rss", out.rss);
|
||||
SCAN("AnonHugePages", out.anonhugepages);
|
||||
SCAN("Private_Hugetlb", out.private_hugetlb);
|
||||
SCAN("Shared_Hugetlb", out.shared_hugetlb);
|
||||
SCAN("Swap", out.swap);
|
||||
int i = 0;
|
||||
#undef SCAN
|
||||
// scan some flags too
|
||||
if (strncmp(_line, "VmFlags:", 8) == 0) {
|
||||
#define SCAN(flag) { out.flag = (::strstr(_line + 8, " " #flag) != nullptr); }
|
||||
SCAN(rd);
|
||||
SCAN(wr);
|
||||
SCAN(ex);
|
||||
SCAN(nr);
|
||||
SCAN(sh);
|
||||
SCAN(hg);
|
||||
SCAN(ht);
|
||||
SCAN(nh);
|
||||
#undef SCAN
|
||||
}
|
||||
}
|
||||
|
||||
// Starts or continues parsing. Returns true on success,
|
||||
// false on EOF or on error.
|
||||
bool ProcSmapsParser::parse_next(ProcSmapsInfo& out) {
|
||||
|
||||
// Information about a single mapping reaches across several lines.
|
||||
out.reset();
|
||||
|
||||
// Read header line, unless we already read it
|
||||
if (_line[0] == '\0') {
|
||||
if (!read_line()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
assert(is_header_line(), "Not a header line: \"%s\".", _line);
|
||||
scan_header_line(out);
|
||||
|
||||
// Now read until we encounter the next header line or EOF or an error.
|
||||
bool ok = false, stop = false;
|
||||
do {
|
||||
ok = read_line();
|
||||
stop = !ok || is_header_line();
|
||||
if (!stop) {
|
||||
scan_additional_line(out);
|
||||
}
|
||||
} while (!stop);
|
||||
|
||||
return ok;
|
||||
}
|
91
src/hotspot/os/linux/procMapsParser.hpp
Normal file
91
src/hotspot/os/linux/procMapsParser.hpp
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Red Hat, Inc. and/or its affiliates.
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OS_LINUX_PROCMAPSPARSER_HPP
|
||||
#define OS_LINUX_PROCMAPSPARSER_HPP
|
||||
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
// This header exposes two simple parsers for /proc/pid/maps and
|
||||
// /proc/pid/smaps.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// FILE* f = fopen(...)
|
||||
// ProcSMapsParser parser(f);
|
||||
// ProcSMapsInfo info;
|
||||
// while (parser.parse_next(info)) { ... }
|
||||
|
||||
struct ProcSmapsInfo {
|
||||
void* from;
|
||||
void* to;
|
||||
char prot[20 + 1];
|
||||
char filename[1024 + 1];
|
||||
size_t kernelpagesize;
|
||||
size_t rss;
|
||||
size_t private_hugetlb;
|
||||
size_t shared_hugetlb;
|
||||
size_t anonhugepages;
|
||||
size_t swap;
|
||||
bool rd, wr, ex;
|
||||
bool sh; // shared
|
||||
bool nr; // no reserve
|
||||
bool hg; // thp-advised
|
||||
bool ht; // uses hugetlb pages
|
||||
bool nh; // thp forbidden
|
||||
|
||||
size_t vsize() const {
|
||||
return from < to ? pointer_delta(to, from, 1) : 0;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
from = to = nullptr;
|
||||
prot[0] = filename[0] = '\0';
|
||||
kernelpagesize = rss = private_hugetlb = shared_hugetlb = anonhugepages = swap = 0;
|
||||
rd = wr = ex = sh = nr = hg = ht = nh = false;
|
||||
}
|
||||
};
|
||||
|
||||
class ProcSmapsParser {
|
||||
FILE* _f;
|
||||
const size_t _linelen;
|
||||
char* _line;
|
||||
|
||||
bool read_line(); // sets had_error in case of error
|
||||
bool is_header_line();
|
||||
void scan_header_line(ProcSmapsInfo& out);
|
||||
void scan_additional_line(ProcSmapsInfo& out);
|
||||
|
||||
public:
|
||||
|
||||
ProcSmapsParser(FILE* f);
|
||||
~ProcSmapsParser();
|
||||
|
||||
// Starts or continues parsing. Returns true on success,
|
||||
// false on EOF or on error.
|
||||
bool parse_next(ProcSmapsInfo& out);
|
||||
};
|
||||
|
||||
#endif // OS_LINUX_PROCMAPSPARSER_HPP
|
@ -27,8 +27,9 @@
|
||||
|
||||
#ifdef LINUX
|
||||
|
||||
#include "logging/logAsyncWriter.hpp"
|
||||
#include "gc/shared/collectedHeap.hpp"
|
||||
#include "logging/logAsyncWriter.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "nmt/memflags.hpp"
|
||||
@ -42,7 +43,6 @@
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
// Note: throughout this code we will use the term "VMA" for OS system level memory mapping
|
||||
@ -160,13 +160,6 @@ public:
|
||||
bool fill_from_nmt() {
|
||||
return VirtualMemoryTracker::walk_virtual_memory(this);
|
||||
}
|
||||
|
||||
void print_on(outputStream* st) const {
|
||||
for (size_t i = 0; i < _count; i ++) {
|
||||
st->print_cr(PTR_FORMAT "-" PTR_FORMAT " %s", p2i(_ranges[i].from), p2i(_ranges[i].to),
|
||||
NMTUtil::flag_to_enum_name(_flags[i]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/////// Thread information //////////////////////////
|
||||
@ -198,7 +191,16 @@ struct GCThreadClosure : public ThreadClosure {
|
||||
};
|
||||
|
||||
static void print_thread_details(uintx thread_id, const char* name, outputStream* st) {
|
||||
st->print("(" UINTX_FORMAT " \"%s\")", (uintx)thread_id, name);
|
||||
// avoid commas and spaces in output to ease post-processing via awk
|
||||
char tmp[64];
|
||||
stringStream ss(tmp, sizeof(tmp));
|
||||
ss.print(":" UINTX_FORMAT "-%s", (uintx)thread_id, name);
|
||||
for (int i = 0; tmp[i] != '\0'; i++) {
|
||||
if (!isalnum(tmp[i])) {
|
||||
tmp[i] = '-';
|
||||
}
|
||||
}
|
||||
st->print_raw(tmp);
|
||||
}
|
||||
|
||||
// Given a region [from, to), if it intersects a known thread stack, print detail infos about that thread.
|
||||
@ -230,41 +232,18 @@ static void print_thread_details_for_supposed_stack_address(const void* from, co
|
||||
|
||||
///////////////
|
||||
|
||||
static void print_legend(outputStream* st) {
|
||||
#define DO(flag, shortname, text) st->print_cr("%10s %s", shortname, text);
|
||||
MappingPrintSession::MappingPrintSession(outputStream* st, const CachedNMTInformation& nmt_info) :
|
||||
_out(st), _nmt_info(nmt_info)
|
||||
{}
|
||||
|
||||
void MappingPrintSession::print_nmt_flag_legend() const {
|
||||
#define DO(flag, shortname, text) _out->indent(); _out->print_cr("%10s: %s", shortname, text);
|
||||
NMT_FLAGS_DO(DO)
|
||||
#undef DO
|
||||
}
|
||||
|
||||
MappingPrintClosure::MappingPrintClosure(outputStream* st, bool human_readable, const CachedNMTInformation& nmt_info) :
|
||||
_out(st), _human_readable(human_readable),
|
||||
_total_count(0), _total_vsize(0), _nmt_info(nmt_info)
|
||||
{}
|
||||
|
||||
void MappingPrintClosure::do_it(const MappingPrintInformation* info) {
|
||||
|
||||
_total_count++;
|
||||
|
||||
const void* const vma_from = info->from();
|
||||
const void* const vma_to = info->to();
|
||||
|
||||
// print from, to
|
||||
_out->print(PTR_FORMAT " - " PTR_FORMAT " ", p2i(vma_from), p2i(vma_to));
|
||||
const size_t size = pointer_delta(vma_to, vma_from, 1);
|
||||
_total_vsize += size;
|
||||
|
||||
// print mapping size
|
||||
if (_human_readable) {
|
||||
_out->print(PROPERFMT " ", PROPERFMTARGS(size));
|
||||
} else {
|
||||
_out->print("%11zu", size);
|
||||
}
|
||||
|
||||
assert(info->from() <= info->to(), "Invalid VMA");
|
||||
_out->fill_to(53);
|
||||
info->print_OS_specific_details(_out);
|
||||
_out->fill_to(70);
|
||||
|
||||
bool MappingPrintSession::print_nmt_info_for_region(const void* vma_from, const void* vma_to) const {
|
||||
int num_printed = 0;
|
||||
// print NMT information, if available
|
||||
if (MemTracker::enabled()) {
|
||||
// Correlate vma region (from, to) with NMT region(s) we collected previously.
|
||||
@ -273,59 +252,33 @@ void MappingPrintClosure::do_it(const MappingPrintInformation* info) {
|
||||
for (int i = 0; i < mt_number_of_types; i++) {
|
||||
const MEMFLAGS flag = (MEMFLAGS)i;
|
||||
if (flags.has_flag(flag)) {
|
||||
if (num_printed > 0) {
|
||||
_out->put(',');
|
||||
}
|
||||
_out->print("%s", get_shortname_for_nmt_flag(flag));
|
||||
if (flag == mtThreadStack) {
|
||||
print_thread_details_for_supposed_stack_address(vma_from, vma_to, _out);
|
||||
}
|
||||
_out->print(" ");
|
||||
num_printed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print file name, if available
|
||||
const char* f = info->filename();
|
||||
if (f != nullptr) {
|
||||
_out->print_raw(f);
|
||||
}
|
||||
_out->cr();
|
||||
return num_printed > 0;
|
||||
}
|
||||
|
||||
void MemMapPrinter::print_header(outputStream* st) {
|
||||
st->print(
|
||||
#ifdef _LP64
|
||||
// 0x0000000000000000 - 0x0000000000000000
|
||||
"from to "
|
||||
#else
|
||||
// 0x00000000 - 0x00000000
|
||||
"from to "
|
||||
#endif
|
||||
);
|
||||
// Print platform-specific columns
|
||||
pd_print_header(st);
|
||||
}
|
||||
|
||||
void MemMapPrinter::print_all_mappings(outputStream* st, bool human_readable) {
|
||||
// First collect all NMT information
|
||||
void MemMapPrinter::print_all_mappings(outputStream* st) {
|
||||
CachedNMTInformation nmt_info;
|
||||
nmt_info.fill_from_nmt();
|
||||
DEBUG_ONLY(nmt_info.print_on(st);)
|
||||
st->print_cr("Memory mappings:");
|
||||
if (!MemTracker::enabled()) {
|
||||
st->cr();
|
||||
st->print_cr(" (NMT is disabled, will not annotate mappings).");
|
||||
// Prepare NMT info cache. But only do so if we print individual mappings,
|
||||
// otherwise, we won't need it and can save that work.
|
||||
if (MemTracker::enabled()) {
|
||||
nmt_info.fill_from_nmt();
|
||||
} else {
|
||||
st->print_cr("NMT is disabled. VM info not available.");
|
||||
}
|
||||
st->cr();
|
||||
|
||||
print_legend(st);
|
||||
st->print_cr("(*) - Mapping contains data from multiple regions");
|
||||
st->cr();
|
||||
|
||||
pd_print_header(st);
|
||||
MappingPrintClosure closure(st, human_readable, nmt_info);
|
||||
pd_iterate_all_mappings(closure);
|
||||
st->print_cr("Total: " UINTX_FORMAT " mappings with a total vsize of %zu (" PROPERFMT ")",
|
||||
closure.total_count(), closure.total_vsize(), PROPERFMTARGS(closure.total_vsize()));
|
||||
MappingPrintSession session(st, nmt_info);
|
||||
pd_print_all_mappings(session);
|
||||
}
|
||||
|
||||
#endif // LINUX
|
||||
|
@ -35,39 +35,20 @@
|
||||
class outputStream;
|
||||
class CachedNMTInformation;
|
||||
|
||||
class MappingPrintInformation {
|
||||
const void* const _from;
|
||||
const void* const _to;
|
||||
public:
|
||||
MappingPrintInformation(const void* from, const void* to) : _from(from), _to(to) {}
|
||||
const void* from() const { return _from; }
|
||||
const void* to() const { return _to; }
|
||||
// Will be called for each mapping before VM annotations are printed.
|
||||
virtual void print_OS_specific_details(outputStream* st) const {}
|
||||
// If mapping is backed by a file, the name of that file
|
||||
virtual const char* filename() const { return nullptr; }
|
||||
};
|
||||
|
||||
class MappingPrintClosure {
|
||||
class MappingPrintSession {
|
||||
outputStream* const _out;
|
||||
const bool _human_readable;
|
||||
uintx _total_count;
|
||||
size_t _total_vsize;
|
||||
const CachedNMTInformation& _nmt_info;
|
||||
public:
|
||||
MappingPrintClosure(outputStream* st, bool human_readable, const CachedNMTInformation& nmt_info);
|
||||
void do_it(const MappingPrintInformation* info); // returns false if timeout reached.
|
||||
uintx total_count() const { return _total_count; }
|
||||
size_t total_vsize() const { return _total_vsize; }
|
||||
MappingPrintSession(outputStream* st, const CachedNMTInformation& nmt_info);
|
||||
bool print_nmt_info_for_region(const void* from, const void* to) const;
|
||||
void print_nmt_flag_legend() const;
|
||||
outputStream* out() const { return _out; }
|
||||
};
|
||||
|
||||
class MemMapPrinter : public AllStatic {
|
||||
static void pd_print_header(outputStream* st);
|
||||
static void print_header(outputStream* st);
|
||||
static void pd_iterate_all_mappings(MappingPrintClosure& closure);
|
||||
static void pd_print_all_mappings(const MappingPrintSession& session);
|
||||
public:
|
||||
static void mark_page_malloced(const void* p, MEMFLAGS f);
|
||||
static void print_all_mappings(outputStream* st, bool human_readable);
|
||||
static void print_all_mappings(outputStream* st);
|
||||
};
|
||||
|
||||
#endif // LINUX
|
||||
|
@ -487,7 +487,7 @@ void before_exit(JavaThread* thread, bool halt) {
|
||||
CodeCache::write_perf_map();
|
||||
}
|
||||
if (PrintMemoryMapAtExit) {
|
||||
MemMapPrinter::print_all_mappings(tty, false);
|
||||
MemMapPrinter::print_all_mappings(tty);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -395,7 +395,13 @@ void AttachListenerThread::thread_entry(JavaThread* thread, TRAPS) {
|
||||
}
|
||||
|
||||
ResourceMark rm;
|
||||
bufferedStream st;
|
||||
// jcmd output can get lengthy. As long as we miss jcmd continuous streaming output
|
||||
// and instead just send the output in bulk, make sure large command output does not
|
||||
// cause asserts. We still retain a max cap, but dimensioned in a way that makes it
|
||||
// highly unlikely we should ever hit it under normal conditions.
|
||||
constexpr size_t initial_size = 1 * M;
|
||||
constexpr size_t max_size = 3 * G;
|
||||
bufferedStream st(initial_size, max_size);
|
||||
jint res = JNI_OK;
|
||||
|
||||
// handle special detachall operation
|
||||
|
@ -1180,23 +1180,17 @@ void CompilationMemoryStatisticDCmd::execute(DCmdSource source, TRAPS) {
|
||||
|
||||
#ifdef LINUX
|
||||
|
||||
SystemMapDCmd::SystemMapDCmd(outputStream* output, bool heap) :
|
||||
DCmdWithParser(output, heap),
|
||||
_human_readable("-H", "Human readable format", "BOOLEAN", false, "false") {
|
||||
_dcmdparser.add_dcmd_option(&_human_readable);
|
||||
}
|
||||
SystemMapDCmd::SystemMapDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
|
||||
|
||||
void SystemMapDCmd::execute(DCmdSource source, TRAPS) {
|
||||
MemMapPrinter::print_all_mappings(output(), _human_readable.value());
|
||||
MemMapPrinter::print_all_mappings(output());
|
||||
}
|
||||
|
||||
static constexpr char default_filename[] = "vm_memory_map_<pid>.txt";
|
||||
|
||||
SystemDumpMapDCmd::SystemDumpMapDCmd(outputStream* output, bool heap) :
|
||||
DCmdWithParser(output, heap),
|
||||
_human_readable("-H", "Human readable format", "BOOLEAN", false, "false"),
|
||||
DCmdWithParser(output, heap),
|
||||
_filename("-F", "file path", "STRING", false, default_filename) {
|
||||
_dcmdparser.add_dcmd_option(&_human_readable);
|
||||
_dcmdparser.add_dcmd_option(&_filename);
|
||||
}
|
||||
|
||||
@ -1214,7 +1208,7 @@ void SystemDumpMapDCmd::execute(DCmdSource source, TRAPS) {
|
||||
if (!MemTracker::enabled()) {
|
||||
output()->print_cr("(NMT is disabled, will not annotate mappings).");
|
||||
}
|
||||
MemMapPrinter::print_all_mappings(&fs, _human_readable.value());
|
||||
MemMapPrinter::print_all_mappings(&fs);
|
||||
// For the readers convenience, resolve path name.
|
||||
char tmp[JVM_MAXPATHLEN];
|
||||
const char* absname = os::Posix::realpath(name, tmp, sizeof(tmp));
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -983,16 +983,14 @@ public:
|
||||
|
||||
#ifdef LINUX
|
||||
|
||||
class SystemMapDCmd : public DCmdWithParser {
|
||||
DCmdArgument<bool> _human_readable;
|
||||
class SystemMapDCmd : public DCmd {
|
||||
public:
|
||||
static int num_arguments() { return 1; }
|
||||
SystemMapDCmd(outputStream* output, bool heap);
|
||||
static const char* name() { return "System.map"; }
|
||||
static const char* description() {
|
||||
return "Prints an annotated process memory map of the VM process (linux only).";
|
||||
}
|
||||
static const char* impact() { return "Low"; }
|
||||
static const char* impact() { return "Medium; can be high for very large java heaps."; }
|
||||
static const JavaPermission permission() {
|
||||
JavaPermission p = {"java.lang.management.ManagementPermission",
|
||||
"control", nullptr};
|
||||
@ -1002,16 +1000,15 @@ public:
|
||||
};
|
||||
|
||||
class SystemDumpMapDCmd : public DCmdWithParser {
|
||||
DCmdArgument<bool> _human_readable;
|
||||
DCmdArgument<char*> _filename;
|
||||
public:
|
||||
static int num_arguments() { return 2; }
|
||||
static int num_arguments() { return 1; }
|
||||
SystemDumpMapDCmd(outputStream* output, bool heap);
|
||||
static const char* name() { return "System.dump_map"; }
|
||||
static const char* description() {
|
||||
return "Dumps an annotated process memory map to an output file (linux only).";
|
||||
}
|
||||
static const char* impact() { return "Low"; }
|
||||
static const char* impact() { return "Medium; can be high for very large java heaps."; }
|
||||
static const JavaPermission permission() {
|
||||
JavaPermission p = {"java.lang.management.ManagementPermission",
|
||||
"control", nullptr};
|
||||
|
@ -191,9 +191,10 @@ void outputStream::print_raw(const char* str, size_t len) {
|
||||
write(str, len);
|
||||
}
|
||||
|
||||
void outputStream::fill_to(int col) {
|
||||
int need_fill = col - position();
|
||||
int outputStream::fill_to(int col) {
|
||||
const int need_fill = MAX2(col - position(), 0);
|
||||
sp(need_fill);
|
||||
return need_fill;
|
||||
}
|
||||
|
||||
void outputStream::move_to(int col, int slop, int min_space) {
|
||||
@ -1037,7 +1038,7 @@ void bufferedStream::write(const char* s, size_t len) {
|
||||
const size_t reasonable_cap = MAX2(100 * M, buffer_max * 2);
|
||||
if (end > reasonable_cap) {
|
||||
// In debug VM, assert right away.
|
||||
assert(false, "Exceeded max buffer size for this string.");
|
||||
assert(false, "Exceeded max buffer size for this string (\"%.200s...\").", buffer);
|
||||
// Release VM: silently truncate. We do this since these kind of errors
|
||||
// are both difficult to predict with testing (depending on logging content)
|
||||
// and usually not serious enough to kill a production VM for it.
|
||||
|
@ -101,7 +101,7 @@ class outputStream : public CHeapObjBase {
|
||||
void dec(int n) { _indentation -= n; };
|
||||
int indentation() const { return _indentation; }
|
||||
void set_indentation(int i) { _indentation = i; }
|
||||
void fill_to(int col);
|
||||
int fill_to(int col);
|
||||
void move_to(int col, int slop = 6, int min_space = 2);
|
||||
|
||||
// Automatic indentation:
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, Red Hat, Inc. and/or its affiliates.
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2024, Red Hat, Inc. and/or its affiliates.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,6 @@ import jdk.test.lib.dcmd.JMXExecutor;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -41,9 +40,9 @@ import java.util.regex.Pattern;
|
||||
* java.compiler
|
||||
* java.management
|
||||
* jdk.internal.jvmstat/sun.jvmstat.monitor
|
||||
* @run testng SystemDumpMapTest
|
||||
* @run testng/othervm -XX:+UsePerfData SystemDumpMapTest
|
||||
*/
|
||||
public class SystemDumpMapTest {
|
||||
public class SystemDumpMapTest extends SystemMapTestBase {
|
||||
|
||||
private void run_test(CommandExecutor executor, boolean useDefaultFileName) {
|
||||
|
||||
@ -64,18 +63,22 @@ public class SystemDumpMapTest {
|
||||
boolean NMTOff = output.contains("NMT is disabled");
|
||||
String regexBase = ".*0x\\p{XDigit}+ - 0x\\p{XDigit}+ +\\d+";
|
||||
HashSet<Pattern> patterns = new HashSet<>();
|
||||
patterns.add(Pattern.compile(regexBase + ".*jvm.*"));
|
||||
if (!NMTOff) { // expect VM annotations if NMT is on
|
||||
patterns.add(Pattern.compile(regexBase + ".*JAVAHEAP.*"));
|
||||
patterns.add(Pattern.compile(regexBase + ".*META.*"));
|
||||
patterns.add(Pattern.compile(regexBase + ".*CODE.*"));
|
||||
patterns.add(Pattern.compile(regexBase + ".*STACK.*main.*"));
|
||||
for (String s: shouldMatchUnconditionally) {
|
||||
patterns.add(Pattern.compile(s));
|
||||
}
|
||||
if (!NMTOff) { // expect VM annotations if NMT is on
|
||||
for (String s: shouldMatchIfNMTIsEnabled) {
|
||||
patterns.add(Pattern.compile(s));
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
String line = reader.readLine();
|
||||
if (line != null) {
|
||||
System.out.println(" " + line);
|
||||
for (Pattern pat : patterns) {
|
||||
if (pat.matcher(line).matches()) {
|
||||
System.out.println(">>> matches " + pat);
|
||||
patterns.remove(pat);
|
||||
break;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, Red Hat, Inc. and/or its affiliates.
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2024, Red Hat, Inc. and/or its affiliates.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,13 +27,6 @@ import jdk.test.lib.dcmd.CommandExecutor;
|
||||
import jdk.test.lib.dcmd.JMXExecutor;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test of diagnostic command System.map
|
||||
@ -43,21 +36,19 @@ import java.util.regex.Pattern;
|
||||
* java.compiler
|
||||
* java.management
|
||||
* jdk.internal.jvmstat/sun.jvmstat.monitor
|
||||
* @run testng SystemMapTest
|
||||
* @run testng/othervm -XX:+UsePerfData SystemMapTest
|
||||
*/
|
||||
public class SystemMapTest {
|
||||
public class SystemMapTest extends SystemMapTestBase {
|
||||
public void run(CommandExecutor executor) {
|
||||
OutputAnalyzer output = executor.execute("System.map");
|
||||
output.reportDiagnosticSummary();
|
||||
boolean NMTOff = output.contains("NMT is disabled");
|
||||
|
||||
String regexBase = ".*0x\\p{XDigit}+ - 0x\\p{XDigit}+ +\\d+";
|
||||
output.shouldMatch(regexBase + ".*jvm.*");
|
||||
for (String s: shouldMatchUnconditionally) {
|
||||
output.shouldMatch(s);
|
||||
}
|
||||
if (!NMTOff) { // expect VM annotations if NMT is on
|
||||
output.shouldMatch(regexBase + ".*JAVAHEAP.*");
|
||||
output.shouldMatch(regexBase + ".*META.*");
|
||||
output.shouldMatch(regexBase + ".*CODE.*");
|
||||
output.shouldMatch(regexBase + ".*STACK.*main.*");
|
||||
for (String s: shouldMatchIfNMTIsEnabled) {
|
||||
output.shouldMatch(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, Red Hat, Inc. and/or its affiliates.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
public class SystemMapTestBase {
|
||||
|
||||
// e.g.
|
||||
// 0x00000007ff800000-0x00000007ff91a000 1155072 rw-p 1155072 0 4K com JAVAHEAP /shared/projects/openjdk/jdk-jdk/output-fastdebug/images/jdk/lib/server/classes.jsa
|
||||
private static final String range = "0x\\p{XDigit}+-0x\\p{XDigit}+";
|
||||
private static final String space = " +";
|
||||
private static final String someSize = "\\d+";
|
||||
private static final String pagesize = "(4K|8K|16K|64K|2M|16M|64M)";
|
||||
private static final String prot = "[rwsxp-]+";
|
||||
|
||||
private static final String regexBase = range + space +
|
||||
someSize + space +
|
||||
prot + space +
|
||||
someSize + space +
|
||||
someSize + space +
|
||||
pagesize + space;
|
||||
|
||||
private static final String regexBase_committed = regexBase + "com.*";
|
||||
private static final String regexBase_shared_and_committed = regexBase + "shrd,com.*";
|
||||
|
||||
protected static final String shouldMatchUnconditionally[] = {
|
||||
// java launcher
|
||||
regexBase_committed + "/bin/java",
|
||||
// libjvm
|
||||
regexBase_committed + "/lib/.*/libjvm.so",
|
||||
// vdso library, should be part of all user space apps on all architectures OpenJDK supports.
|
||||
regexBase_committed + "\\[vdso\\]",
|
||||
// we should see the hs-perf data file, and it should appear as shared as well as committed
|
||||
regexBase_shared_and_committed + "hsperfdata_.*"
|
||||
};
|
||||
|
||||
protected static final String shouldMatchIfNMTIsEnabled[] = {
|
||||
regexBase_committed + "JAVAHEAP.*",
|
||||
// metaspace
|
||||
regexBase_committed + "META.*",
|
||||
// parts of metaspace should be uncommitted
|
||||
regexBase + "-" + space + "META.*",
|
||||
// code cache
|
||||
regexBase_committed + "CODE.*",
|
||||
// Main thread stack
|
||||
regexBase_committed + "STACK.*main.*"
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user