blob: 9388d20ab560c5ddc90e7f2629a1a9751697090c [file]
/*
* Copyright (c) 2024, 2025, 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.
*
*/
#include "nmt/memMapPrinter.hpp"
#include "os_windows.hpp"
#include "runtime/vm_version.hpp"
#include "utilities/ostream.hpp"
#include <limits.h>
#include <winnt.h>
#include <memoryapi.h>
#include <psapi.h>
/* maximum number of mapping records returned */
static const int MAX_REGIONS_RETURNED = 1000000;
class MappingInfo {
public:
stringStream _ap_buffer;
stringStream _state_buffer;
stringStream _protect_buffer;
stringStream _type_buffer;
char _file_name[MAX_PATH];
MappingInfo() {}
void process(MEMORY_BASIC_INFORMATION& mem_info) {
_ap_buffer.reset();
_state_buffer.reset();
_protect_buffer.reset();
_type_buffer.reset();
get_protect_string(_ap_buffer, mem_info.AllocationProtect);
get_state_string(_state_buffer, mem_info);
get_protect_string(_protect_buffer, mem_info.Protect);
get_type_string(_type_buffer, mem_info);
_file_name[0] = 0;
if (mem_info.Type == MEM_IMAGE) {
HMODULE hModule = 0;
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(mem_info.AllocationBase), &hModule)) {
GetModuleFileName(hModule, _file_name, sizeof(_file_name));
}
}
}
void get_protect_string(outputStream& out, DWORD prot) {
const char read_c = prot & (PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY) ? 'r' : '-';
const char write_c = prot & (PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) ? 'w' : '-';
const char execute_c = prot & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) ? 'x' : '-';
out.print("%c%c%c", read_c, write_c, execute_c);
if (prot & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) {
out.put('c');
}
if (prot & PAGE_GUARD) {
out.put('g');
}
if (prot & PAGE_NOCACHE) {
out.put('n');
}
if (prot & PAGE_WRITECOMBINE) {
out.put('W');
}
const DWORD bits = PAGE_NOACCESS | PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE
| PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE
| PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE;
if ((prot & bits) != prot) {
out.print_cr("Unknown Windows memory protection value: 0x%x unknown bits: 0x%x", prot, prot & ~bits);
assert(false, "Unknown Windows memory protection value: 0x%x unknown bits: 0x%x", prot, prot & ~bits);
}
}
void get_state_string(outputStream& out, MEMORY_BASIC_INFORMATION& mem_info) {
if (mem_info.State == MEM_COMMIT) {
out.put('c');
} else if (mem_info.State == MEM_FREE) {
out.put('f');
} else if (mem_info.State == MEM_RESERVE) {
out.put('r');
} else {
out.print_cr("Unknown Windows memory state value: 0x%x", mem_info.State);
assert(false, "Unknown Windows memory state value: 0x%x", mem_info.State);
}
}
void get_type_string(outputStream& out, MEMORY_BASIC_INFORMATION& mem_info) {
if (mem_info.Type == MEM_IMAGE) {
out.print("img");
} else if (mem_info.Type == MEM_MAPPED) {
out.print("map");
} else if (mem_info.Type == MEM_PRIVATE) {
out.print("pvt");
} else if (mem_info.Type == 0 && mem_info.State == MEM_FREE) {
out.print("---");
} else {
out.print_cr("Unknown Windows memory type 0x%x", mem_info.Type);
assert(false, "Unknown Windows memory type 0x%x", mem_info.Type);
}
}
};
class MappingInfoSummary {
unsigned _num_mappings;
size_t _total_region_size; // combined resident set size
size_t _total_committed; // combined committed size
class WinOsInfo : public os::win32 {
public:
static void printOsInfo(outputStream* st) {
st->print("OS:");
os::win32::print_windows_version(st);
os::win32::print_uptime_info(st);
VM_Version::print_platform_virtualization_info(st);
os::print_memory_info(st);
}
};
public:
MappingInfoSummary() : _num_mappings(0), _total_region_size(0),
_total_committed(0) {}
void add_mapping(const MEMORY_BASIC_INFORMATION& mem_info, const MappingInfo& mapping_info) {
if (mem_info.State != MEM_FREE) {
_num_mappings++;
_total_region_size += mem_info.RegionSize;
_total_committed += mem_info.State == MEM_COMMIT ? mem_info.RegionSize : 0;
}
}
void print_on(const MappingPrintSession& session) const {
outputStream* st = session.out();
WinOsInfo::printOsInfo(st);
st->print_cr("current process reserved memory: " PROPERFMT, PROPERFMTARGS(_total_region_size));
st->print_cr("current process committed memory: " PROPERFMT, PROPERFMTARGS(_total_committed));
st->print_cr("current process region count: " PROPERFMT, PROPERFMTARGS(_num_mappings));
}
};
class MappingInfoPrinter {
const MappingPrintSession& _session;
public:
MappingInfoPrinter(const MappingPrintSession& session) :
_session(session)
{}
void print_single_mapping(const MEMORY_BASIC_INFORMATION& mem_info, const MappingInfo& mapping_info) const {
outputStream* st = _session.out();
#define INDENT_BY(n) \
if (st->fill_to(n) == 0) { \
st->print(" "); \
}
st->print(PTR_FORMAT "-" PTR_FORMAT, mem_info.BaseAddress, static_cast<const char*>(mem_info.BaseAddress) + mem_info.RegionSize);
INDENT_BY(38);
st->print("%12zu", mem_info.RegionSize);
INDENT_BY(51);
st->print("%s", mapping_info._protect_buffer.base());
INDENT_BY(57);
st->print("%s-%s", mapping_info._state_buffer.base(), mapping_info._type_buffer.base());
INDENT_BY(63);
st->print("%#11llx", reinterpret_cast<const unsigned long long>(mem_info.BaseAddress) - reinterpret_cast<const unsigned long long>(mem_info.AllocationBase));
INDENT_BY(72);
if (_session.print_nmt_info_for_region(mem_info.BaseAddress, static_cast<const char*>(mem_info.BaseAddress) + mem_info.RegionSize)) {
st->print(" ");
}
st->print_raw(mapping_info._file_name);
#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(" rwx: read / write / execute");
st->print_cr(" c: copy on write");
st->print_cr(" g: guard");
st->print_cr(" n: no cache");
st->print_cr(" W: write combine");
st->print_cr("state: region state and type:");
st->print_cr(" state: committed / reserved");
st->print_cr(" type: image / mapped / private");
st->print_cr("offset: offset from start of allocation block");
st->print_cr("vminfo: VM information (requires NMT)");
st->print_cr("file: file mapped, if mapping is not anonymous");
{
StreamIndentor si(st, 16);
_session.print_nmt_flag_legend();
}
}
void print_header() const {
outputStream* st = _session.out();
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
// 0x00007ffb24565000-0x00007ffb24a7e000 5345280 r-- c-img 0x1155000 C:\work\jdk\build\fastdebug\jdk\bin\server\jvm.dll
st->print_cr("from to vsize prot state offset vminfo/file");
st->print_cr("===========================================================================================");
}
};
void MemMapPrinter::pd_print_all_mappings(const MappingPrintSession& session) {
HANDLE hProcess = GetCurrentProcess();
MappingInfoPrinter printer(session);
MappingInfoSummary summary;
outputStream* const st = session.out();
printer.print_legend();
st->cr();
printer.print_header();
MEMORY_BASIC_INFORMATION mem_info;
MappingInfo mapping_info;
int region_count = 0;
::memset(&mem_info, 0, sizeof(mem_info));
for (char* ptr = 0; VirtualQueryEx(hProcess, ptr, &mem_info, sizeof(mem_info)) == sizeof(mem_info); ) {
assert(mem_info.RegionSize > 0, "RegionSize is not greater than zero");
if (++region_count > MAX_REGIONS_RETURNED) {
st->print_cr("limit of %d regions reached (results inaccurate)", region_count);
break;
}
mapping_info.process(mem_info);
if (mem_info.State != MEM_FREE) {
printer.print_single_mapping(mem_info, mapping_info);
summary.add_mapping(mem_info, mapping_info);
}
ptr += mem_info.RegionSize;
::memset(&mem_info, 0, sizeof(mem_info));
}
st->cr();
summary.print_on(session);
st->cr();
}