8322475: Extend printing for System.map

Reviewed-by: sgehwolf, jsjolen
This commit is contained in:
Thomas Stuefe 2024-07-03 16:08:34 +00:00
parent 19a8a2baa9
commit 8aaec37ace
14 changed files with 518 additions and 204 deletions

@ -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);
}

@ -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;
}

@ -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.*"
};
}