8318636: Add jcmd to print annotated process memory map
Reviewed-by: jsjolen, gziemski
This commit is contained in:
parent
e035637a4c
commit
6f863b2a1b
src/hotspot
os/linux
share
test/hotspot/jtreg/serviceability/dcmd/vm
84
src/hotspot/os/linux/memMapPrinter_linux.cpp
Normal file
84
src/hotspot/os/linux/memMapPrinter_linux.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
|
||||
#include "runtime/os.hpp"
|
||||
#include "nmt/memMapPrinter.hpp"
|
||||
#include "utilities/globalDefinitions.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;
|
||||
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);
|
||||
}
|
||||
|
||||
const char* filename() const override { return _info.filename; }
|
||||
};
|
||||
|
||||
void MemMapPrinter::pd_print_header(outputStream* st) {
|
||||
st->print_cr("size prot offset What");
|
||||
}
|
||||
|
||||
void MemMapPrinter::pd_iterate_all_mappings(MappingPrintClosure& closure) {
|
||||
FILE* f = os::fopen("/proc/self/maps", "r");
|
||||
if (f == nullptr) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
::fclose(f);
|
||||
}
|
@ -178,7 +178,6 @@ class AsyncLogWriter : public NonJavaThread {
|
||||
NonJavaThread::pre_run();
|
||||
log_debug(logging, thread)("starting AsyncLog Thread tid = " INTX_FORMAT, os::current_thread_id());
|
||||
}
|
||||
const char* name() const override { return "AsyncLog Thread"; }
|
||||
const char* type_name() const override { return "AsyncLogWriter"; }
|
||||
void print_on(outputStream* st) const override {
|
||||
st->print("\"%s\" ", name());
|
||||
@ -203,6 +202,8 @@ class AsyncLogWriter : public NonJavaThread {
|
||||
static AsyncLogWriter* instance();
|
||||
static void initialize();
|
||||
static void flush();
|
||||
|
||||
const char* name() const override { return "AsyncLog Thread"; }
|
||||
};
|
||||
|
||||
#endif // SHARE_LOGGING_LOGASYNCWRITER_HPP
|
||||
|
54
src/hotspot/share/nmt/memFlagBitmap.hpp
Normal file
54
src/hotspot/share/nmt/memFlagBitmap.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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 SHARE_NMT_MEMFLAGBITMAP_HPP
|
||||
#define SHARE_NMT_MEMFLAGBITMAP_HPP
|
||||
|
||||
#include "memory/allocation.hpp" // for mt_number_of_types
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
class MemFlagBitmap {
|
||||
uint32_t _v;
|
||||
STATIC_ASSERT(sizeof(_v) * BitsPerByte >= mt_number_of_types);
|
||||
|
||||
public:
|
||||
MemFlagBitmap(uint32_t v = 0) : _v(v) {}
|
||||
MemFlagBitmap(const MemFlagBitmap& o) : _v(o._v) {}
|
||||
|
||||
uint32_t raw_value() const { return _v; }
|
||||
|
||||
void set_flag(MEMFLAGS f) {
|
||||
const int bitno = (int)f;
|
||||
_v |= nth_bit(bitno);
|
||||
}
|
||||
|
||||
bool has_flag(MEMFLAGS f) const {
|
||||
const int bitno = (int)f;
|
||||
return _v & nth_bit(bitno);
|
||||
}
|
||||
|
||||
bool has_any() const { return _v > 0; }
|
||||
};
|
||||
|
||||
#endif // SHARE_NMT_NMTUSAGE_HPP
|
309
src/hotspot/share/nmt/memMapPrinter.cpp
Normal file
309
src/hotspot/share/nmt/memMapPrinter.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
|
||||
#ifdef LINUX
|
||||
|
||||
#include "logging/logAsyncWriter.hpp"
|
||||
#include "gc/shared/collectedHeap.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "runtime/nonJavaThread.hpp"
|
||||
#include "runtime/osThread.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "nmt/memFlagBitmap.hpp"
|
||||
#include "nmt/memMapPrinter.hpp"
|
||||
#include "nmt/memTracker.hpp"
|
||||
#include "nmt/virtualMemoryTracker.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
|
||||
|
||||
/// NMT mechanics
|
||||
|
||||
// Short, clear, descriptive names for all possible markers. Note that we only expect to see
|
||||
// those that have been used with mmap. Flags left out are printed with their nmt flag name.
|
||||
#define NMT_FLAGS_DO(f) \
|
||||
/* flag, short, description */ \
|
||||
f(mtGCCardSet, "CARDTBL", "GC Card table") \
|
||||
f(mtClassShared, "CDS", "CDS archives") \
|
||||
f(mtClass, "CLASS", "Class Space") \
|
||||
f(mtCode, "CODE", "Code Heap") \
|
||||
f(mtGC, "GC", "GC support data (e.g. bitmaps)") \
|
||||
f(mtInternal, "INTERN", "Internal") \
|
||||
f(mtJavaHeap, "JAVAHEAP", "Java Heap") \
|
||||
f(mtOther, "JDK", "allocated by JDK libraries other than VM") \
|
||||
f(mtMetaspace, "META", "Metaspace nodes (non-class)") \
|
||||
f(mtSafepoint, "POLL", "Polling pages") \
|
||||
f(mtThreadStack, "STACK", "(known) Thread Stack") \
|
||||
f(mtTest, "TEST", "JVM internal test mappings")
|
||||
//end
|
||||
|
||||
static const char* get_shortname_for_nmt_flag(MEMFLAGS f) {
|
||||
#define DO(flag, shortname, text) if (flag == f) return shortname;
|
||||
NMT_FLAGS_DO(DO)
|
||||
#undef DO
|
||||
return NMTUtil::flag_to_enum_name(f);
|
||||
}
|
||||
|
||||
/// NMT virtual memory
|
||||
|
||||
static bool range_intersects(const void* from1, const void* to1, const void* from2, const void* to2) {
|
||||
return MAX2(from1, from2) < MIN2(to1, to2);
|
||||
}
|
||||
|
||||
// A Cache that correlates range with MEMFLAG, optimized to be iterated quickly
|
||||
// (cache friendly).
|
||||
class CachedNMTInformation : public VirtualMemoryWalker {
|
||||
struct Range { const void* from; const void* to; };
|
||||
// We keep ranges apart from flags since that prevents the padding a combined
|
||||
// structure would have, and it allows for faster iteration of ranges since more
|
||||
// of them fit into a cache line.
|
||||
Range* _ranges;
|
||||
MEMFLAGS* _flags;
|
||||
uintx _count, _capacity;
|
||||
public:
|
||||
CachedNMTInformation() : _ranges(nullptr), _flags(nullptr), _count(0), _capacity(0) {}
|
||||
|
||||
~CachedNMTInformation() {
|
||||
ALLOW_C_FUNCTION(free, ::free(_ranges);)
|
||||
ALLOW_C_FUNCTION(free, ::free(_flags);)
|
||||
}
|
||||
|
||||
bool add(const void* from, const void* to, MEMFLAGS f) {
|
||||
// We rely on NMT regions being sorted by base
|
||||
assert(_count == 0 || (from >= _ranges[_count - 1].to), "NMT regions unordered?");
|
||||
// we can just fold two regions if they are adjacent and have the same flag.
|
||||
if (_count > 0 && from == _ranges[_count - 1].to && f == _flags[_count - 1]) {
|
||||
_ranges[_count - 1].to = to;
|
||||
return true;
|
||||
}
|
||||
if (_count == _capacity) {
|
||||
// Enlarge if needed
|
||||
const uintx new_capacity = MAX2((uintx)4096, 2 * _capacity);
|
||||
// Unfortunately, we need to allocate manually, raw, since we must prevent NMT deadlocks (ThreadCritical).
|
||||
ALLOW_C_FUNCTION(realloc, _ranges = (Range*)::realloc(_ranges, new_capacity * sizeof(Range));)
|
||||
ALLOW_C_FUNCTION(realloc, _flags = (MEMFLAGS*)::realloc(_flags, new_capacity * sizeof(MEMFLAGS));)
|
||||
if (_ranges == nullptr || _flags == nullptr) {
|
||||
// In case of OOM lets make no fuzz. Just return.
|
||||
return false;
|
||||
}
|
||||
_capacity = new_capacity;
|
||||
}
|
||||
assert(_capacity > _count, "Sanity");
|
||||
_ranges[_count] = Range { from, to };
|
||||
_flags[_count] = f;
|
||||
_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Given a vma [from, to), find all regions that intersect with this vma and
|
||||
// return their collective flags.
|
||||
MemFlagBitmap lookup(const void* from, const void* to) const {
|
||||
MemFlagBitmap bm;
|
||||
for(uintx i = 0; i < _count; i++) {
|
||||
if (range_intersects(from, to, _ranges[i].from, _ranges[i].to)) {
|
||||
bm.set_flag(_flags[i]);
|
||||
} else if (from < _ranges[i].to) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bm;
|
||||
}
|
||||
|
||||
bool do_allocation_site(const ReservedMemoryRegion* rgn) override {
|
||||
// Cancel iteration if we run out of memory (add returns false);
|
||||
return add(rgn->base(), rgn->end(), rgn->flag());
|
||||
}
|
||||
|
||||
// Iterate all NMT virtual memory regions and fill this cache.
|
||||
bool fill_from_nmt() {
|
||||
return VirtualMemoryTracker::walk_virtual_memory(this);
|
||||
}
|
||||
};
|
||||
|
||||
/////// Thread information //////////////////////////
|
||||
|
||||
// Given a VMA [from, to) and a thread, check if vma intersects with thread stack
|
||||
static bool vma_touches_thread_stack(const void* from, const void* to, const Thread* t) {
|
||||
// Java thread stacks (and sometimes also other threads) have guard pages. Therefore they typically occupy
|
||||
// at least two distinct neighboring VMAs. Therefore we typically have a 1:n relationshipt between thread
|
||||
// stack and vma.
|
||||
// Very rarely however is a VMA backing a thread stack folded together with another adjacent VMA by the
|
||||
// kernel. That can happen, e.g., for non-java threads that don't have guard pages.
|
||||
// Therefore we go for the simplest way here and check for intersection between VMA and thread stack.
|
||||
return range_intersects(from, to, (const void*)t->stack_end(), (const void*)t->stack_base());
|
||||
}
|
||||
|
||||
struct GCThreadClosure : public ThreadClosure {
|
||||
bool _found;
|
||||
uintx _tid;
|
||||
const void* const _from;
|
||||
const void* const _to;
|
||||
GCThreadClosure(const void* from, const void* to) : _found(false), _tid(0), _from(from), _to(to) {}
|
||||
void do_thread(Thread* t) override {
|
||||
if (_tid == 0 && t != nullptr && vma_touches_thread_stack(_from, _to, t)) {
|
||||
_found = true;
|
||||
_tid = t->osthread()->thread_id();
|
||||
// lemme stooop! No way to signal stop :(
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void print_thread_details(uintx thread_id, const char* name, outputStream* st) {
|
||||
st->print("(" UINTX_FORMAT " \"%s\")", (uintx)thread_id, name);
|
||||
}
|
||||
|
||||
// Given a region [from, to), if it intersects a known thread stack, print detail infos about that thread.
|
||||
static void print_thread_details_for_supposed_stack_address(const void* from, const void* to, outputStream* st) {
|
||||
|
||||
#define HANDLE_THREAD(T) \
|
||||
if (T != nullptr && vma_touches_thread_stack(from, to, T)) { \
|
||||
print_thread_details((uintx)(T->osthread()->thread_id()), T->name(), st); \
|
||||
return; \
|
||||
}
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread* t = jtiwh.next(); ) {
|
||||
HANDLE_THREAD(t);
|
||||
}
|
||||
HANDLE_THREAD(VMThread::vm_thread());
|
||||
HANDLE_THREAD(WatcherThread::watcher_thread());
|
||||
HANDLE_THREAD(AsyncLogWriter::instance());
|
||||
#undef HANDLE_THREAD
|
||||
|
||||
if (Universe::heap() != nullptr) {
|
||||
GCThreadClosure cl(from, to);
|
||||
Universe::heap()->gc_threads_do(&cl);
|
||||
if (cl._found) {
|
||||
print_thread_details(cl._tid, "GC Thread", st);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////
|
||||
|
||||
static void print_legend(outputStream* st) {
|
||||
#define DO(flag, shortname, text) st->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);
|
||||
|
||||
// print NMT information, if available
|
||||
if (MemTracker::enabled()) {
|
||||
// Correlate vma region (from, to) with NMT region(s) we collected previously.
|
||||
const MemFlagBitmap flags = _nmt_info.lookup(vma_from, vma_to);
|
||||
if (flags.has_any()) {
|
||||
for (int i = 0; i < mt_number_of_types; i++) {
|
||||
const MEMFLAGS flag = (MEMFLAGS)i;
|
||||
if (flags.has_flag(flag)) {
|
||||
_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(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print file name, if available
|
||||
const char* f = info->filename();
|
||||
if (f != nullptr) {
|
||||
_out->print_raw(f);
|
||||
}
|
||||
_out->cr();
|
||||
}
|
||||
|
||||
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
|
||||
CachedNMTInformation nmt_info;
|
||||
nmt_info.fill_from_nmt();
|
||||
|
||||
st->print_cr("Memory mappings:");
|
||||
if (!MemTracker::enabled()) {
|
||||
st->cr();
|
||||
st->print_cr(" (NMT is disabled, will not annotate mappings).");
|
||||
}
|
||||
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()));
|
||||
}
|
||||
|
||||
#endif // LINUX
|
75
src/hotspot/share/nmt/memMapPrinter.hpp
Normal file
75
src/hotspot/share/nmt/memMapPrinter.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_SERVICES_MEMMAPPRINTER_HPP
|
||||
#define SHARE_SERVICES_MEMMAPPRINTER_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
#ifdef LINUX
|
||||
|
||||
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 {
|
||||
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; }
|
||||
};
|
||||
|
||||
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);
|
||||
public:
|
||||
static void mark_page_malloced(const void* p, MEMFLAGS f);
|
||||
static void print_all_mappings(outputStream* st, bool human_readable);
|
||||
};
|
||||
|
||||
#endif // LINUX
|
||||
|
||||
#endif // SHARE_SERVICES_MEMMAPPRINTER_HPP
|
@ -40,6 +40,8 @@
|
||||
#include "memory/metaspace/metaspaceDCmd.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "nmt/memMapPrinter.hpp"
|
||||
#include "nmt/memTracker.hpp"
|
||||
#include "nmt/nmtDCmd.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
#include "oops/objArrayOop.inline.hpp"
|
||||
@ -67,8 +69,10 @@
|
||||
#include "utilities/macros.hpp"
|
||||
#include "utilities/parseInteger.hpp"
|
||||
#ifdef LINUX
|
||||
#include "os_posix.hpp"
|
||||
#include "mallocInfoDcmd.hpp"
|
||||
#include "trimCHeapDCmd.hpp"
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
static void loadAgentModule(TRAPS) {
|
||||
@ -133,6 +137,8 @@ void DCmd::register_dcmds(){
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PerfMapDCmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TrimCLibcHeapDCmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<MallocInfoDcmd>(full_export, true, false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SystemMapDCmd>(full_export, true,false));
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SystemDumpMapDCmd>(full_export, true,false));
|
||||
#endif // LINUX
|
||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeHeapAnalyticsDCmd>(full_export, true, false));
|
||||
|
||||
@ -1151,3 +1157,45 @@ void CompilationMemoryStatisticDCmd::execute(DCmdSource source, TRAPS) {
|
||||
const size_t minsize = _minsize.has_value() ? _minsize.value()._size : 0;
|
||||
CompilationMemoryStatistic::print_all_by_size(output(), human_readable, minsize);
|
||||
}
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
void SystemMapDCmd::execute(DCmdSource source, TRAPS) {
|
||||
MemMapPrinter::print_all_mappings(output(), _human_readable.value());
|
||||
}
|
||||
|
||||
SystemDumpMapDCmd::SystemDumpMapDCmd(outputStream* output, bool heap) :
|
||||
DCmdWithParser(output, heap),
|
||||
_human_readable("-H", "Human readable format", "BOOLEAN", false, "false"),
|
||||
_filename("-F", "file path (defaults: \"vm_memory_map_<pid>.txt\")", "STRING", false) {
|
||||
_dcmdparser.add_dcmd_option(&_human_readable);
|
||||
_dcmdparser.add_dcmd_option(&_filename);
|
||||
}
|
||||
|
||||
void SystemDumpMapDCmd::execute(DCmdSource source, TRAPS) {
|
||||
stringStream default_name;
|
||||
default_name.print("vm_memory_map_%d.txt", os::current_process_id());
|
||||
const char* name = _filename.is_set() ? _filename.value() : default_name.base();
|
||||
fileStream fs(name);
|
||||
if (fs.is_open()) {
|
||||
if (!MemTracker::enabled()) {
|
||||
output()->print_cr("(NMT is disabled, will not annotate mappings).");
|
||||
}
|
||||
MemMapPrinter::print_all_mappings(&fs, _human_readable.value());
|
||||
// For the readers convenience, resolve path name.
|
||||
char tmp[JVM_MAXPATHLEN];
|
||||
const char* absname = os::Posix::realpath(name, tmp, sizeof(tmp));
|
||||
name = absname != nullptr ? absname : name;
|
||||
output()->print_cr("Memory map dumped to \"%s\".", name);
|
||||
} else {
|
||||
output()->print_cr("Failed to open \"%s\" for writing (%s).", name, os::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LINUX
|
||||
|
@ -978,4 +978,45 @@ public:
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
#ifdef LINUX
|
||||
|
||||
class SystemMapDCmd : public DCmdWithParser {
|
||||
DCmdArgument<bool> _human_readable;
|
||||
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 JavaPermission permission() {
|
||||
JavaPermission p = {"java.lang.management.ManagementPermission",
|
||||
"control", nullptr};
|
||||
return p;
|
||||
}
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
class SystemDumpMapDCmd : public DCmdWithParser {
|
||||
DCmdArgument<bool> _human_readable;
|
||||
DCmdArgument<char*> _filename;
|
||||
public:
|
||||
static int num_arguments() { return 2; }
|
||||
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 JavaPermission permission() {
|
||||
JavaPermission p = {"java.lang.management.ManagementPermission",
|
||||
"control", nullptr};
|
||||
return p;
|
||||
}
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
};
|
||||
|
||||
#endif // LINUX
|
||||
|
||||
#endif // SHARE_SERVICES_DIAGNOSTICCOMMAND_HPP
|
||||
|
111
test/hotspot/jtreg/serviceability/dcmd/vm/SystemDumpMapTest.java
Normal file
111
test/hotspot/jtreg/serviceability/dcmd/vm/SystemDumpMapTest.java
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 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.
|
||||
*/
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import jdk.test.lib.dcmd.CommandExecutor;
|
||||
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;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test of diagnostic command System.map
|
||||
* @library /test/lib
|
||||
* @requires (os.family=="linux")
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.compiler
|
||||
* java.management
|
||||
* jdk.internal.jvmstat/sun.jvmstat.monitor
|
||||
* @run testng SystemDumpMapTest
|
||||
*/
|
||||
public class SystemDumpMapTest {
|
||||
|
||||
private void run_test(CommandExecutor executor, boolean useDefaultFileName) {
|
||||
|
||||
String filenameOption = useDefaultFileName ? "" : "-F=test-map.txt";
|
||||
|
||||
OutputAnalyzer output = executor.execute("System.dump_map " + filenameOption);
|
||||
output.reportDiagnosticSummary();
|
||||
|
||||
String filename = useDefaultFileName ?
|
||||
output.firstMatch("Memory map dumped to \"(\\S*vm_memory_map_\\d+\\.txt)\".*", 1) :
|
||||
output.firstMatch("Memory map dumped to \"(\\S*test-map.txt)\".*", 1);
|
||||
|
||||
if (filename == null) {
|
||||
throw new RuntimeException("Did not find dump file in output.\n");
|
||||
}
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
|
||||
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.*"));
|
||||
}
|
||||
do {
|
||||
String line = reader.readLine();
|
||||
if (line != null) {
|
||||
for (Pattern pat : patterns) {
|
||||
if (pat.matcher(line).matches()) {
|
||||
patterns.remove(pat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (patterns.size() > 0);
|
||||
|
||||
if (patterns.size() > 0) {
|
||||
System.out.println("Missing patterns in dump:");
|
||||
for (Pattern pat : patterns) {
|
||||
System.out.println(pat);
|
||||
}
|
||||
throw new RuntimeException("Missing patterns");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void run(CommandExecutor executor) {
|
||||
run_test(executor, false);
|
||||
run_test(executor, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmx() {
|
||||
run(new JMXExecutor());
|
||||
}
|
||||
}
|
68
test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTest.java
Normal file
68
test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTest.java
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 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.
|
||||
*/
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
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
|
||||
* @library /test/lib
|
||||
* @requires (os.family=="linux")
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.compiler
|
||||
* java.management
|
||||
* jdk.internal.jvmstat/sun.jvmstat.monitor
|
||||
* @run testng SystemMapTest
|
||||
*/
|
||||
public class SystemMapTest {
|
||||
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.*");
|
||||
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.*");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmx() {
|
||||
run(new JMXExecutor());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user