mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8362566: Use -Xlog:aot+map to print contents of existing AOT cache
Reviewed-by: vlivanov, kvn
This commit is contained in:
parent
0d54329304
commit
aaff9dec24
930
src/hotspot/share/cds/aotMapLogger.cpp
Normal file
930
src/hotspot/share/cds/aotMapLogger.cpp
Normal file
@ -0,0 +1,930 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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 "cds/aotMapLogger.hpp"
|
||||
#include "cds/archiveHeapWriter.hpp"
|
||||
#include "cds/cdsConfig.hpp"
|
||||
#include "cds/filemap.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#include "classfile/vmClasses.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "memory/metaspaceClosure.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/method.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
#include "runtime/globals_extension.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
bool AOTMapLogger::_is_logging_at_bootstrap;
|
||||
bool AOTMapLogger::_is_runtime_logging;
|
||||
intx AOTMapLogger::_buffer_to_requested_delta;
|
||||
intx AOTMapLogger::_requested_to_mapped_metadata_delta;
|
||||
size_t AOTMapLogger::_num_root_segments;
|
||||
size_t AOTMapLogger::_num_obj_arrays_logged;
|
||||
GrowableArrayCHeap<AOTMapLogger::FakeOop, mtClass>* AOTMapLogger::_roots;
|
||||
ArchiveHeapInfo* AOTMapLogger::_dumptime_heap_info;
|
||||
|
||||
class AOTMapLogger::RequestedMetadataAddr {
|
||||
address _raw_addr;
|
||||
|
||||
public:
|
||||
RequestedMetadataAddr(address raw_addr) : _raw_addr(raw_addr) {}
|
||||
|
||||
address raw_addr() const { return _raw_addr; }
|
||||
|
||||
Klass* to_real_klass() const {
|
||||
if (_raw_addr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (_is_runtime_logging) {
|
||||
return (Klass*)(_raw_addr + _requested_to_mapped_metadata_delta);
|
||||
} else {
|
||||
ArchiveBuilder* builder = ArchiveBuilder::current();
|
||||
address buffered_addr = builder->requested_to_buffered(_raw_addr);
|
||||
address klass = builder->get_source_addr(buffered_addr);
|
||||
return (Klass*)klass;
|
||||
}
|
||||
}
|
||||
}; // AOTMapLogger::RequestedMetadataAddr
|
||||
|
||||
void AOTMapLogger::ergo_initialize() {
|
||||
if (!CDSConfig::is_dumping_archive() && CDSConfig::is_using_archive() && log_is_enabled(Info, aot, map)) {
|
||||
_is_logging_at_bootstrap = true;
|
||||
if (FLAG_IS_DEFAULT(ArchiveRelocationMode)) {
|
||||
FLAG_SET_ERGO(ArchiveRelocationMode, 0);
|
||||
} else if (ArchiveRelocationMode != 0) {
|
||||
log_warning(aot, map)("Addresses in the AOT map may be incorrect for -XX:ArchiveRelocationMode=%d.", ArchiveRelocationMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AOTMapLogger::dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo,
|
||||
ArchiveHeapInfo* heap_info,
|
||||
char* bitmap, size_t bitmap_size_in_bytes) {
|
||||
_is_runtime_logging = false;
|
||||
_buffer_to_requested_delta = ArchiveBuilder::current()->buffer_to_requested_delta();
|
||||
_num_root_segments = mapinfo->heap_root_segments().count();
|
||||
_dumptime_heap_info = heap_info;
|
||||
|
||||
log_file_header(mapinfo);
|
||||
|
||||
DumpRegion* rw_region = &builder->_rw_region;
|
||||
DumpRegion* ro_region = &builder->_ro_region;
|
||||
|
||||
dumptime_log_metaspace_region("rw region", rw_region, &builder->_rw_src_objs);
|
||||
dumptime_log_metaspace_region("ro region", ro_region, &builder->_ro_src_objs);
|
||||
|
||||
address bitmap_end = address(bitmap + bitmap_size_in_bytes);
|
||||
log_region_range("bitmap", address(bitmap), bitmap_end, nullptr);
|
||||
log_as_hex((address)bitmap, bitmap_end, nullptr);
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
if (heap_info->is_used()) {
|
||||
dumptime_log_heap_region(heap_info);
|
||||
}
|
||||
#endif
|
||||
|
||||
log_info(aot, map)("[End of AOT cache map]");
|
||||
}
|
||||
|
||||
// This class is used to find the location and type of all the
|
||||
// archived metaspace objects.
|
||||
class AOTMapLogger::RuntimeGatherArchivedMetaspaceObjs : public UniqueMetaspaceClosure {
|
||||
GrowableArrayCHeap<ArchivedObjInfo, mtClass> _objs;
|
||||
|
||||
static int compare_objs_by_addr(ArchivedObjInfo* a, ArchivedObjInfo* b) {
|
||||
intx diff = a->_src_addr - b->_src_addr;
|
||||
if (diff < 0) {
|
||||
return -1;
|
||||
} else if (diff == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
GrowableArrayCHeap<ArchivedObjInfo, mtClass>* objs() { return &_objs; }
|
||||
|
||||
virtual bool do_unique_ref(Ref* ref, bool read_only) {
|
||||
ArchivedObjInfo info;
|
||||
info._src_addr = ref->obj();
|
||||
info._buffered_addr = ref->obj();
|
||||
info._requested_addr = ref->obj();
|
||||
info._bytes = ref->size() * BytesPerWord;
|
||||
info._type = ref->msotype();
|
||||
_objs.append(info);
|
||||
|
||||
return true; // keep iterating
|
||||
}
|
||||
|
||||
void finish() {
|
||||
UniqueMetaspaceClosure::finish();
|
||||
_objs.sort(compare_objs_by_addr);
|
||||
}
|
||||
}; // AOTMapLogger::RuntimeGatherArchivedMetaspaceObjs
|
||||
|
||||
void AOTMapLogger::runtime_log(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo) {
|
||||
_is_runtime_logging = true;
|
||||
_requested_to_mapped_metadata_delta = static_mapinfo->relocation_delta();
|
||||
|
||||
ResourceMark rm;
|
||||
RuntimeGatherArchivedMetaspaceObjs gatherer;
|
||||
|
||||
if (log_is_enabled(Debug, aot, map)) {
|
||||
// The metaspace objects in the AOT cache are stored as a stream of bytes. For space
|
||||
// saving, we don't store a complete index that tells us where one object ends and
|
||||
// another object starts. There's also no type information.
|
||||
//
|
||||
// However, we can rebuild our index by iterating over all the objects using
|
||||
// MetaspaceClosure, starting from the dictionary of Klasses in SystemDictionaryShared.
|
||||
GrowableArray<Klass*> klasses;
|
||||
SystemDictionaryShared::get_all_archived_classes(/*is_static*/true, &klasses);
|
||||
if (dynamic_mapinfo != nullptr) {
|
||||
SystemDictionaryShared::get_all_archived_classes(/*is_static*/false, &klasses);
|
||||
}
|
||||
|
||||
for (int i = 0; i < klasses.length(); i++) {
|
||||
gatherer.push(klasses.adr_at(i));
|
||||
}
|
||||
gatherer.finish();
|
||||
}
|
||||
|
||||
runtime_log(static_mapinfo, gatherer.objs());
|
||||
if (dynamic_mapinfo != nullptr) {
|
||||
runtime_log(dynamic_mapinfo, gatherer.objs());
|
||||
}
|
||||
}
|
||||
|
||||
void AOTMapLogger::runtime_log(FileMapInfo* mapinfo, GrowableArrayCHeap<ArchivedObjInfo, mtClass>* objs) {
|
||||
log_file_header(mapinfo);
|
||||
|
||||
runtime_log_metaspace_regions(mapinfo, objs);
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
if (mapinfo->has_heap_region() && CDSConfig::is_loading_heap()) {
|
||||
_num_root_segments = mapinfo->heap_root_segments().count();
|
||||
runtime_log_heap_region(mapinfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
log_info(aot, map)("[End of map]");
|
||||
}
|
||||
|
||||
void AOTMapLogger::dumptime_log_metaspace_region(const char* name, DumpRegion* region,
|
||||
const ArchiveBuilder::SourceObjList* src_objs) {
|
||||
address region_base = address(region->base());
|
||||
address region_top = address(region->top());
|
||||
log_region_range(name, region_base, region_top, region_base + _buffer_to_requested_delta);
|
||||
if (log_is_enabled(Debug, aot, map)) {
|
||||
GrowableArrayCHeap<ArchivedObjInfo, mtClass> objs;
|
||||
for (int i = 0; i < src_objs->objs()->length(); i++) {
|
||||
ArchiveBuilder::SourceObjInfo* src_info = src_objs->at(i);
|
||||
ArchivedObjInfo info;
|
||||
info._src_addr = src_info->source_addr();
|
||||
info._buffered_addr = src_info->buffered_addr();
|
||||
info._requested_addr = info._buffered_addr + _buffer_to_requested_delta;
|
||||
info._bytes = src_info->size_in_bytes();
|
||||
info._type = src_info->msotype();
|
||||
objs.append(info);
|
||||
}
|
||||
|
||||
log_metaspace_objects_impl(address(region->base()), address(region->end()), &objs, 0, objs.length());
|
||||
}
|
||||
}
|
||||
|
||||
void AOTMapLogger::runtime_log_metaspace_regions(FileMapInfo* mapinfo, GrowableArrayCHeap<ArchivedObjInfo, mtClass>* objs) {
|
||||
FileMapRegion* rw = mapinfo->region_at(MetaspaceShared::rw);
|
||||
FileMapRegion* ro = mapinfo->region_at(MetaspaceShared::ro);
|
||||
|
||||
address rw_base = address(rw->mapped_base());
|
||||
address rw_end = address(rw->mapped_end());
|
||||
address ro_base = address(ro->mapped_base());
|
||||
address ro_end = address(ro->mapped_end());
|
||||
|
||||
int first_rw_index = -1;
|
||||
int first_ro_index = -1;
|
||||
int last_ro_index = -1;
|
||||
|
||||
if (log_is_enabled(Debug, aot, map)) {
|
||||
int i = 0;
|
||||
for (; i < objs->length(); i++) {
|
||||
address p = objs->at(i)._src_addr;
|
||||
if (p < rw_base) {
|
||||
// We are printing the dynamic archive but found an object in the static archive
|
||||
precond(!mapinfo->is_static());
|
||||
continue;
|
||||
}
|
||||
if (first_rw_index < 0) {
|
||||
first_rw_index = i;
|
||||
continue;
|
||||
}
|
||||
if (p < ro_base) {
|
||||
continue;
|
||||
}
|
||||
if (first_ro_index < 0) {
|
||||
first_ro_index = i;
|
||||
continue;
|
||||
}
|
||||
if (p < ro_end) {
|
||||
continue;
|
||||
} else {
|
||||
last_ro_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (last_ro_index < 0) {
|
||||
last_ro_index = objs->length();
|
||||
}
|
||||
|
||||
log_region_range("rw", rw_base, rw_end, rw_base - _requested_to_mapped_metadata_delta);
|
||||
if (log_is_enabled(Debug, aot, map)) {
|
||||
log_metaspace_objects_impl(rw_base, rw_end, objs, first_rw_index, first_ro_index);
|
||||
}
|
||||
|
||||
log_region_range("ro", ro_base, ro_end, ro_base - _requested_to_mapped_metadata_delta);
|
||||
if (log_is_enabled(Debug, aot, map)) {
|
||||
log_metaspace_objects_impl(ro_base, ro_end, objs, first_ro_index, last_ro_index);
|
||||
}
|
||||
}
|
||||
|
||||
void AOTMapLogger::log_file_header(FileMapInfo* mapinfo) {
|
||||
const char* type;
|
||||
if (mapinfo->is_static()) {
|
||||
if (CDSConfig::new_aot_flags_used()) {
|
||||
type = "AOT cache";
|
||||
} else {
|
||||
type = "Static CDS archive";
|
||||
}
|
||||
} else {
|
||||
type = "Dynamic CDS archive";
|
||||
}
|
||||
|
||||
log_info(aot, map)("%s map for %s", type, mapinfo->full_path());
|
||||
|
||||
address header = address(mapinfo->header());
|
||||
address header_end = header + mapinfo->header()->header_size();
|
||||
|
||||
log_region_range("header", header, header_end, nullptr);
|
||||
LogStreamHandle(Info, aot, map) lsh;
|
||||
mapinfo->print(&lsh);
|
||||
log_as_hex(header, header_end, nullptr);
|
||||
}
|
||||
|
||||
// Log information about a region, whose address at dump time is [base .. top). At
|
||||
// runtime, this region will be mapped to requested_base. requested_base is nullptr if this
|
||||
// region will be mapped at os-selected addresses (such as the bitmap region), or will
|
||||
// be accessed with os::read (the header).
|
||||
void AOTMapLogger::log_region_range(const char* name, address base, address top, address requested_base) {
|
||||
size_t size = top - base;
|
||||
base = requested_base;
|
||||
if (requested_base == nullptr) {
|
||||
top = (address)size;
|
||||
} else {
|
||||
top = requested_base + size;
|
||||
}
|
||||
log_info(aot, map)("[%-18s " PTR_FORMAT " - " PTR_FORMAT " %9zu bytes]",
|
||||
name, p2i(base), p2i(top), size);
|
||||
}
|
||||
|
||||
#define _LOG_PREFIX PTR_FORMAT ": @@ %-17s %d"
|
||||
|
||||
void AOTMapLogger::log_metaspace_objects_impl(address region_base, address region_end, GrowableArrayCHeap<ArchivedObjInfo, mtClass>* objs,
|
||||
int start_idx, int end_idx) {
|
||||
address last_obj_base = region_base;
|
||||
address last_obj_end = region_base;
|
||||
Thread* current = Thread::current();
|
||||
|
||||
for (int i = start_idx; i < end_idx; i++) {
|
||||
ArchivedObjInfo& info = objs->at(i);
|
||||
address src = info._src_addr;
|
||||
address buffered_addr = info._buffered_addr;
|
||||
address requested_addr = info._requested_addr;
|
||||
int bytes = info._bytes;
|
||||
MetaspaceObj::Type type = info._type;
|
||||
const char* type_name = MetaspaceObj::type_name(type);
|
||||
|
||||
log_as_hex(last_obj_base, buffered_addr, last_obj_base + _buffer_to_requested_delta);
|
||||
|
||||
switch (type) {
|
||||
case MetaspaceObj::ClassType:
|
||||
log_klass((Klass*)src, requested_addr, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::ConstantPoolType:
|
||||
log_constant_pool((ConstantPool*)src, requested_addr, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::ConstantPoolCacheType:
|
||||
log_constant_pool_cache((ConstantPoolCache*)src, requested_addr, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::ConstMethodType:
|
||||
log_const_method((ConstMethod*)src, requested_addr, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::MethodType:
|
||||
log_method((Method*)src, requested_addr, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::SymbolType:
|
||||
log_symbol((Symbol*)src, requested_addr, type_name, bytes, current);
|
||||
break;
|
||||
default:
|
||||
log_debug(aot, map)(_LOG_PREFIX, p2i(requested_addr), type_name, bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
last_obj_base = buffered_addr;
|
||||
last_obj_end = buffered_addr + bytes;
|
||||
}
|
||||
|
||||
log_as_hex(last_obj_base, last_obj_end, last_obj_base + _buffer_to_requested_delta);
|
||||
if (last_obj_end < region_end) {
|
||||
log_debug(aot, map)(PTR_FORMAT ": @@ Misc data %zu bytes",
|
||||
p2i(last_obj_end + _buffer_to_requested_delta),
|
||||
size_t(region_end - last_obj_end));
|
||||
log_as_hex(last_obj_end, region_end, last_obj_end + _buffer_to_requested_delta);
|
||||
}
|
||||
}
|
||||
|
||||
void AOTMapLogger::log_constant_pool(ConstantPool* cp, address requested_addr,
|
||||
const char* type_name, int bytes, Thread* current) {
|
||||
ResourceMark rm(current);
|
||||
log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes,
|
||||
cp->pool_holder()->external_name());
|
||||
}
|
||||
|
||||
void AOTMapLogger::log_constant_pool_cache(ConstantPoolCache* cpc, address requested_addr,
|
||||
const char* type_name, int bytes, Thread* current) {
|
||||
ResourceMark rm(current);
|
||||
log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes,
|
||||
cpc->constant_pool()->pool_holder()->external_name());
|
||||
}
|
||||
|
||||
void AOTMapLogger::log_const_method(ConstMethod* cm, address requested_addr, const char* type_name,
|
||||
int bytes, Thread* current) {
|
||||
ResourceMark rm(current);
|
||||
log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, cm->method()->external_name());
|
||||
}
|
||||
|
||||
void AOTMapLogger::log_klass(Klass* k, address requested_addr, const char* type_name,
|
||||
int bytes, Thread* current) {
|
||||
ResourceMark rm(current);
|
||||
log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, k->external_name());
|
||||
}
|
||||
|
||||
void AOTMapLogger::log_method(Method* m, address requested_addr, const char* type_name,
|
||||
int bytes, Thread* current) {
|
||||
ResourceMark rm(current);
|
||||
log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, m->external_name());
|
||||
}
|
||||
|
||||
void AOTMapLogger::log_symbol(Symbol* s, address requested_addr, const char* type_name,
|
||||
int bytes, Thread* current) {
|
||||
ResourceMark rm(current);
|
||||
log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes,
|
||||
s->as_quoted_ascii());
|
||||
}
|
||||
|
||||
#undef _LOG_PREFIX
|
||||
|
||||
// Log all the data [base...top). Pretend that the base address
|
||||
// will be mapped to requested_base at run-time.
|
||||
void AOTMapLogger::log_as_hex(address base, address top, address requested_base, bool is_heap) {
|
||||
assert(top >= base, "must be");
|
||||
|
||||
LogStreamHandle(Trace, aot, map) lsh;
|
||||
if (lsh.is_enabled()) {
|
||||
int unitsize = sizeof(address);
|
||||
if (is_heap && UseCompressedOops) {
|
||||
// This makes the compressed oop pointers easier to read, but
|
||||
// longs and doubles will be split into two words.
|
||||
unitsize = sizeof(narrowOop);
|
||||
}
|
||||
os::print_hex_dump(&lsh, base, top, unitsize, /* print_ascii=*/true, /* bytes_per_line=*/32, requested_base);
|
||||
}
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
// FakeOop (and subclasses FakeMirror, FakeString, FakeObjArray, FakeTypeArray) are used to traverse
|
||||
// and print the (image of) heap objects stored in the AOT cache. These objects are different than regular oops:
|
||||
// - They do not reside inside the range of the heap.
|
||||
// - For +UseCompressedOops: pointers may use a different narrowOop encoding: see FakeOop::read_oop_at(narrowOop*)
|
||||
// - For -UseCompressedOops: pointers are not direct: see FakeOop::read_oop_at(oop*)
|
||||
//
|
||||
// Hence, in general, we cannot use regular oop API (such as oopDesc::obj_field()) on these objects. There
|
||||
// are a few rare case where regular oop API work, but these are all guarded with the raw_oop() method and
|
||||
// should be used with care.
|
||||
class AOTMapLogger::FakeOop {
|
||||
static int _requested_shift;
|
||||
static intx _buffer_to_requested_delta;
|
||||
static address _buffer_start;
|
||||
static address _buffer_end;
|
||||
static uint64_t _buffer_start_narrow_oop; // The encoded narrow oop for the objects at _buffer_start
|
||||
|
||||
address _buffer_addr;
|
||||
|
||||
static void assert_range(address buffer_addr) {
|
||||
assert(_buffer_start <= buffer_addr && buffer_addr < _buffer_end, "range check");
|
||||
}
|
||||
|
||||
address* field_addr(int field_offset) {
|
||||
return (address*)(_buffer_addr + field_offset);
|
||||
}
|
||||
|
||||
protected:
|
||||
RequestedMetadataAddr metadata_field(int field_offset) {
|
||||
return RequestedMetadataAddr(*(address*)(field_addr(field_offset)));
|
||||
}
|
||||
|
||||
// Return an "oop" pointer so we can use APIs that accept regular oops. This
|
||||
// must be used with care, as only a limited number of APIs can work with oops that
|
||||
// live outside of the range of the heap.
|
||||
oop raw_oop() { return cast_to_oop(_buffer_addr); }
|
||||
|
||||
public:
|
||||
static void init_globals(address requested_base, address requested_start, int requested_shift,
|
||||
address buffer_start, address buffer_end) {
|
||||
_requested_shift = requested_shift;
|
||||
_buffer_to_requested_delta = requested_start - buffer_start;
|
||||
_buffer_start = buffer_start;
|
||||
_buffer_end = buffer_end;
|
||||
|
||||
precond(requested_start >= requested_base);
|
||||
if (UseCompressedOops) {
|
||||
_buffer_start_narrow_oop = (uint64_t)(pointer_delta(requested_start, requested_base, 1)) >> _requested_shift;
|
||||
assert(_buffer_start_narrow_oop < 0xffffffff, "sanity");
|
||||
} else {
|
||||
_buffer_start_narrow_oop = 0xdeadbeed;
|
||||
}
|
||||
}
|
||||
|
||||
FakeOop() : _buffer_addr(nullptr) {}
|
||||
|
||||
FakeOop(address buffer_addr) : _buffer_addr(buffer_addr) {
|
||||
if (_buffer_addr != nullptr) {
|
||||
assert_range(_buffer_addr);
|
||||
}
|
||||
}
|
||||
|
||||
FakeMirror& as_mirror();
|
||||
FakeObjArray& as_obj_array();
|
||||
FakeString& as_string();
|
||||
FakeTypeArray& as_type_array();
|
||||
|
||||
RequestedMetadataAddr klass() {
|
||||
address rk = (address)real_klass();
|
||||
if (_is_runtime_logging) {
|
||||
return RequestedMetadataAddr(rk - _requested_to_mapped_metadata_delta);
|
||||
} else {
|
||||
ArchiveBuilder* builder = ArchiveBuilder::current();
|
||||
return builder->to_requested(builder->get_buffered_addr(rk));
|
||||
}
|
||||
}
|
||||
|
||||
Klass* real_klass() {
|
||||
assert(UseCompressedClassPointers, "heap archiving requires UseCompressedClassPointers");
|
||||
if (_is_runtime_logging) {
|
||||
return raw_oop()->klass();
|
||||
} else {
|
||||
return ArchiveHeapWriter::real_klass_of_buffered_oop(_buffer_addr);
|
||||
}
|
||||
}
|
||||
|
||||
// in heap words
|
||||
size_t size() {
|
||||
if (_is_runtime_logging) {
|
||||
return raw_oop()->size_given_klass(real_klass());
|
||||
} else {
|
||||
return ArchiveHeapWriter::size_of_buffered_oop(_buffer_addr);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_array() { return real_klass()->is_array_klass(); }
|
||||
bool is_null() { return _buffer_addr == nullptr; }
|
||||
|
||||
int array_length() {
|
||||
precond(is_array());
|
||||
return arrayOop(raw_oop())->length();
|
||||
}
|
||||
|
||||
address requested_addr() {
|
||||
return _buffer_addr + _buffer_to_requested_delta;
|
||||
}
|
||||
|
||||
uint32_t as_narrow_oop_value() {
|
||||
precond(UseCompressedOops);
|
||||
if (_buffer_addr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t pd = (uint64_t)(pointer_delta(_buffer_addr, _buffer_start, 1));
|
||||
return checked_cast<uint32_t>(_buffer_start_narrow_oop + (pd >> _requested_shift));
|
||||
}
|
||||
|
||||
FakeOop read_oop_at(narrowOop* addr) { // +UseCompressedOops
|
||||
uint64_t n = (uint64_t)(*addr);
|
||||
if (n == 0) {
|
||||
return FakeOop(nullptr);
|
||||
} else {
|
||||
precond(n >= _buffer_start_narrow_oop);
|
||||
address value = _buffer_start + ((n - _buffer_start_narrow_oop) << _requested_shift);
|
||||
return FakeOop(value);
|
||||
}
|
||||
}
|
||||
|
||||
FakeOop read_oop_at(oop* addr) { // -UseCompressedOops
|
||||
address requested_value = cast_from_oop<address>(*addr);
|
||||
if (requested_value == nullptr) {
|
||||
return FakeOop(nullptr);
|
||||
} else {
|
||||
return FakeOop(requested_value - _buffer_to_requested_delta);
|
||||
}
|
||||
}
|
||||
|
||||
FakeOop obj_field(int field_offset) {
|
||||
if (UseCompressedOops) {
|
||||
return read_oop_at(raw_oop()->field_addr<narrowOop>(field_offset));
|
||||
} else {
|
||||
return read_oop_at(raw_oop()->field_addr<oop>(field_offset));
|
||||
}
|
||||
}
|
||||
|
||||
void print_non_oop_field(outputStream* st, fieldDescriptor* fd) {
|
||||
// fd->print_on_for() works for non-oop fields in fake oops
|
||||
precond(fd->field_type() != T_ARRAY && fd->field_type() != T_OBJECT);
|
||||
fd->print_on_for(st, raw_oop());
|
||||
}
|
||||
}; // AOTMapLogger::FakeOop
|
||||
|
||||
class AOTMapLogger::FakeMirror : public AOTMapLogger::FakeOop {
|
||||
public:
|
||||
void print_class_signature_on(outputStream* st);
|
||||
|
||||
Klass* real_mirrored_klass() {
|
||||
RequestedMetadataAddr mirrored_klass = metadata_field(java_lang_Class::klass_offset());
|
||||
return mirrored_klass.to_real_klass();
|
||||
}
|
||||
|
||||
int static_oop_field_count() {
|
||||
return java_lang_Class::static_oop_field_count(raw_oop());
|
||||
}
|
||||
}; // AOTMapLogger::FakeMirror
|
||||
|
||||
class AOTMapLogger::FakeObjArray : public AOTMapLogger::FakeOop {
|
||||
objArrayOop raw_objArrayOop() {
|
||||
return (objArrayOop)raw_oop();
|
||||
}
|
||||
|
||||
public:
|
||||
int length() {
|
||||
return raw_objArrayOop()->length();
|
||||
}
|
||||
FakeOop obj_at(int i) {
|
||||
if (UseCompressedOops) {
|
||||
return read_oop_at(raw_objArrayOop()->obj_at_addr<narrowOop>(i));
|
||||
} else {
|
||||
return read_oop_at(raw_objArrayOop()->obj_at_addr<oop>(i));
|
||||
}
|
||||
}
|
||||
}; // AOTMapLogger::FakeObjArray
|
||||
|
||||
class AOTMapLogger::FakeString : public AOTMapLogger::FakeOop {
|
||||
public:
|
||||
bool is_latin1() {
|
||||
jbyte coder = raw_oop()->byte_field(java_lang_String::coder_offset());
|
||||
assert(CompactStrings || coder == java_lang_String::CODER_UTF16, "Must be UTF16 without CompactStrings");
|
||||
return coder == java_lang_String::CODER_LATIN1;
|
||||
}
|
||||
|
||||
FakeTypeArray value();
|
||||
|
||||
int length();
|
||||
void print_on(outputStream* st, int max_length = MaxStringPrintSize);
|
||||
}; // AOTMapLogger::FakeString
|
||||
|
||||
class AOTMapLogger::FakeTypeArray : public AOTMapLogger::FakeOop {
|
||||
typeArrayOop raw_typeArrayOop() {
|
||||
return (typeArrayOop)raw_oop();
|
||||
}
|
||||
|
||||
public:
|
||||
void print_elements_on(outputStream* st) {
|
||||
TypeArrayKlass::cast(real_klass())->oop_print_elements_on(raw_typeArrayOop(), st);
|
||||
}
|
||||
|
||||
int length() { return raw_typeArrayOop()->length(); }
|
||||
jbyte byte_at(int i) { return raw_typeArrayOop()->byte_at(i); }
|
||||
jchar char_at(int i) { return raw_typeArrayOop()->char_at(i); }
|
||||
}; // AOTMapLogger::FakeTypeArray
|
||||
|
||||
AOTMapLogger::FakeMirror& AOTMapLogger::FakeOop::as_mirror() {
|
||||
precond(real_klass() == vmClasses::Class_klass());
|
||||
return (FakeMirror&)*this;
|
||||
}
|
||||
|
||||
AOTMapLogger::FakeObjArray& AOTMapLogger::FakeOop::as_obj_array() {
|
||||
precond(real_klass()->is_objArray_klass());
|
||||
return (FakeObjArray&)*this;
|
||||
}
|
||||
|
||||
AOTMapLogger::FakeTypeArray& AOTMapLogger::FakeOop::as_type_array() {
|
||||
precond(real_klass()->is_typeArray_klass());
|
||||
return (FakeTypeArray&)*this;
|
||||
}
|
||||
|
||||
AOTMapLogger::FakeString& AOTMapLogger::FakeOop::as_string() {
|
||||
precond(real_klass() == vmClasses::String_klass());
|
||||
return (FakeString&)*this;
|
||||
}
|
||||
|
||||
void AOTMapLogger::FakeMirror::print_class_signature_on(outputStream* st) {
|
||||
ResourceMark rm;
|
||||
RequestedMetadataAddr requested_klass = metadata_field(java_lang_Class::klass_offset());
|
||||
Klass* real_klass = requested_klass.to_real_klass();
|
||||
|
||||
if (real_klass == nullptr) {
|
||||
// This is a primitive mirror (Java expressions of int.class, long.class, void.class, etc);
|
||||
RequestedMetadataAddr requested_array_klass = metadata_field(java_lang_Class::array_klass_offset());
|
||||
Klass* real_array_klass = requested_array_klass.to_real_klass();
|
||||
if (real_array_klass == nullptr) {
|
||||
st->print(" V"); // The special mirror for void.class that doesn't have any representation in C++
|
||||
} else {
|
||||
precond(real_array_klass->is_typeArray_klass());
|
||||
st->print(" %c", real_array_klass->name()->char_at(1));
|
||||
}
|
||||
} else {
|
||||
const char* class_name = real_klass->name()->as_C_string();
|
||||
if (real_klass->is_instance_klass()) {
|
||||
st->print(" L%s;", class_name);
|
||||
} else {
|
||||
st->print(" %s", class_name);
|
||||
}
|
||||
if (real_klass->has_aot_initialized_mirror()) {
|
||||
st->print(" (aot-inited)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AOTMapLogger::FakeTypeArray AOTMapLogger::FakeString::value() {
|
||||
return obj_field(java_lang_String::value_offset()).as_type_array();
|
||||
}
|
||||
|
||||
int AOTMapLogger::FakeString::length() {
|
||||
FakeTypeArray v = value();
|
||||
if (v.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
int arr_length = v.length();
|
||||
if (!is_latin1()) {
|
||||
assert((arr_length & 1) == 0, "should be even for UTF16 string");
|
||||
arr_length >>= 1; // convert number of bytes to number of elements
|
||||
}
|
||||
return arr_length;
|
||||
}
|
||||
|
||||
void AOTMapLogger::FakeString::print_on(outputStream* st, int max_length) {
|
||||
FakeTypeArray v = value();
|
||||
int length = this->length();
|
||||
bool is_latin1 = this->is_latin1();
|
||||
bool abridge = length > max_length;
|
||||
|
||||
st->print("\"");
|
||||
for (int index = 0; index < length; index++) {
|
||||
// If we need to abridge and we've printed half the allowed characters
|
||||
// then jump to the tail of the string.
|
||||
if (abridge && index >= max_length / 2) {
|
||||
st->print(" ... (%d characters ommitted) ... ", length - 2 * (max_length / 2));
|
||||
index = length - (max_length / 2);
|
||||
abridge = false; // only do this once
|
||||
}
|
||||
jchar c = (!is_latin1) ? v.char_at(index) :
|
||||
((jchar) v.byte_at(index)) & 0xff;
|
||||
if (c < ' ') {
|
||||
st->print("\\x%02X", c); // print control characters e.g. \x0A
|
||||
} else {
|
||||
st->print("%c", c);
|
||||
}
|
||||
}
|
||||
st->print("\"");
|
||||
|
||||
if (length > max_length) {
|
||||
st->print(" (abridged) ");
|
||||
}
|
||||
}
|
||||
|
||||
class AOTMapLogger::ArchivedFieldPrinter : public FieldClosure {
|
||||
FakeOop _fake_oop;
|
||||
outputStream* _st;
|
||||
public:
|
||||
ArchivedFieldPrinter(FakeOop fake_oop, outputStream* st) : _fake_oop(fake_oop), _st(st) {}
|
||||
|
||||
void do_field(fieldDescriptor* fd) {
|
||||
_st->print(" - ");
|
||||
BasicType ft = fd->field_type();
|
||||
switch (ft) {
|
||||
case T_ARRAY:
|
||||
case T_OBJECT:
|
||||
{
|
||||
fd->print_on(_st); // print just the name and offset
|
||||
FakeOop field_value = _fake_oop.obj_field(fd->offset());
|
||||
print_oop_info_cr(_st, field_value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_fake_oop.print_non_oop_field(_st, fd); // name, offset, value
|
||||
_st->cr();
|
||||
}
|
||||
}
|
||||
}; // AOTMapLogger::ArchivedFieldPrinter
|
||||
|
||||
int AOTMapLogger::FakeOop::_requested_shift;
|
||||
intx AOTMapLogger::FakeOop::_buffer_to_requested_delta;
|
||||
address AOTMapLogger::FakeOop::_buffer_start;
|
||||
address AOTMapLogger::FakeOop::_buffer_end;
|
||||
uint64_t AOTMapLogger::FakeOop::_buffer_start_narrow_oop;
|
||||
|
||||
void AOTMapLogger::dumptime_log_heap_region(ArchiveHeapInfo* heap_info) {
|
||||
MemRegion r = heap_info->buffer_region();
|
||||
address buffer_start = address(r.start()); // start of the current oop inside the buffer
|
||||
address buffer_end = address(r.end());
|
||||
|
||||
address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)ArchiveHeapWriter::NOCOOPS_REQUESTED_BASE;
|
||||
address requested_start = UseCompressedOops ? ArchiveHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base;
|
||||
int requested_shift = CompressedOops::shift();
|
||||
|
||||
FakeOop::init_globals(requested_base, requested_start, requested_shift, buffer_start, buffer_end);
|
||||
|
||||
log_region_range("heap", buffer_start, buffer_end, requested_start);
|
||||
log_oops(buffer_start, buffer_end);
|
||||
}
|
||||
|
||||
void AOTMapLogger::runtime_log_heap_region(FileMapInfo* mapinfo) {
|
||||
ResourceMark rm;
|
||||
int heap_region_index = MetaspaceShared::hp;
|
||||
FileMapRegion* r = mapinfo->region_at(heap_region_index);
|
||||
size_t alignment = ObjectAlignmentInBytes;
|
||||
|
||||
// Allocate a buffer and read the image of the archived heap region. This buffer is outside
|
||||
// of the real Java heap, so we must use FakeOop to access the contents of the archived heap objects.
|
||||
char* buffer = resource_allocate_bytes(r->used() + alignment);
|
||||
address buffer_start = (address)align_up(buffer, alignment);
|
||||
address buffer_end = buffer_start + r->used();
|
||||
if (!mapinfo->read_region(heap_region_index, (char*)buffer_start, r->used(), /* do_commit = */ false)) {
|
||||
log_error(aot)("Cannot read heap region; AOT map logging of heap objects failed");
|
||||
return;
|
||||
}
|
||||
|
||||
address requested_base = UseCompressedOops ? (address)mapinfo->narrow_oop_base() : mapinfo->heap_region_requested_address();
|
||||
address requested_start = requested_base + r->mapping_offset();
|
||||
int requested_shift = mapinfo->narrow_oop_shift();
|
||||
|
||||
FakeOop::init_globals(requested_base, requested_start, requested_shift, buffer_start, buffer_end);
|
||||
|
||||
log_region_range("heap", buffer_start, buffer_end, requested_start);
|
||||
log_oops(buffer_start, buffer_end);
|
||||
}
|
||||
|
||||
void AOTMapLogger::log_oops(address buffer_start, address buffer_end) {
|
||||
LogStreamHandle(Debug, aot, map) st;
|
||||
if (!st.is_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_roots = new GrowableArrayCHeap<FakeOop, mtClass>();
|
||||
_num_obj_arrays_logged = 0;
|
||||
|
||||
for (address fop = buffer_start; fop < buffer_end; ) {
|
||||
FakeOop fake_oop(fop);
|
||||
st.print(PTR_FORMAT ": @@ Object ", p2i(fake_oop.requested_addr()));
|
||||
print_oop_info_cr(&st, fake_oop, /*print_requested_addr=*/false);
|
||||
|
||||
LogStreamHandle(Trace, aot, map, oops) trace_st;
|
||||
if (trace_st.is_enabled()) {
|
||||
print_oop_details(fake_oop, &trace_st);
|
||||
}
|
||||
|
||||
address next_fop = fop + fake_oop.size() * BytesPerWord;
|
||||
log_as_hex(fop, next_fop, fake_oop.requested_addr(), /*is_heap=*/true);
|
||||
|
||||
fop = next_fop;
|
||||
}
|
||||
|
||||
delete _roots;
|
||||
}
|
||||
|
||||
void AOTMapLogger::print_oop_info_cr(outputStream* st, FakeOop fake_oop, bool print_requested_addr) {
|
||||
if (fake_oop.is_null()) {
|
||||
st->print_cr("null");
|
||||
} else {
|
||||
ResourceMark rm;
|
||||
Klass* real_klass = fake_oop.real_klass();
|
||||
address requested_addr = fake_oop.requested_addr();
|
||||
if (print_requested_addr) {
|
||||
st->print(PTR_FORMAT " ", p2i(requested_addr));
|
||||
}
|
||||
if (UseCompressedOops) {
|
||||
st->print("(0x%08x) ", fake_oop.as_narrow_oop_value());
|
||||
}
|
||||
if (fake_oop.is_array()) {
|
||||
int array_len = fake_oop.array_length();
|
||||
st->print_cr("%s length: %d", real_klass->external_name(), array_len);
|
||||
} else {
|
||||
st->print("%s", real_klass->external_name());
|
||||
|
||||
if (real_klass == vmClasses::String_klass()) {
|
||||
st->print(" ");
|
||||
fake_oop.as_string().print_on(st);
|
||||
} else if (real_klass == vmClasses::Class_klass()) {
|
||||
fake_oop.as_mirror().print_class_signature_on(st);
|
||||
}
|
||||
|
||||
st->cr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print the fields of instanceOops, or the elements of arrayOops
|
||||
void AOTMapLogger::print_oop_details(FakeOop fake_oop, outputStream* st) {
|
||||
Klass* real_klass = fake_oop.real_klass();
|
||||
|
||||
st->print(" - klass: ");
|
||||
real_klass->print_value_on(st);
|
||||
st->print(" " PTR_FORMAT, p2i(fake_oop.klass().raw_addr()));
|
||||
st->cr();
|
||||
|
||||
if (real_klass->is_typeArray_klass()) {
|
||||
fake_oop.as_type_array().print_elements_on(st);
|
||||
} else if (real_klass->is_objArray_klass()) {
|
||||
FakeObjArray fake_obj_array = fake_oop.as_obj_array();
|
||||
bool is_logging_root_segment = _num_obj_arrays_logged < _num_root_segments;
|
||||
|
||||
for (int i = 0; i < fake_obj_array.length(); i++) {
|
||||
FakeOop elm = fake_obj_array.obj_at(i);
|
||||
if (is_logging_root_segment) {
|
||||
st->print(" root[%4d]: ", _roots->length());
|
||||
_roots->append(elm);
|
||||
} else {
|
||||
st->print(" -%4d: ", i);
|
||||
}
|
||||
print_oop_info_cr(st, elm);
|
||||
}
|
||||
_num_obj_arrays_logged ++;
|
||||
} else {
|
||||
st->print_cr(" - fields (%zu words):", fake_oop.size());
|
||||
|
||||
ArchivedFieldPrinter print_field(fake_oop, st);
|
||||
InstanceKlass::cast(real_klass)->print_nonstatic_fields(&print_field);
|
||||
|
||||
if (real_klass == vmClasses::Class_klass()) {
|
||||
FakeMirror fake_mirror = fake_oop.as_mirror();
|
||||
|
||||
st->print(" - signature: ");
|
||||
fake_mirror.print_class_signature_on(st);
|
||||
st->cr();
|
||||
|
||||
Klass* real_mirrored_klass = fake_mirror.real_mirrored_klass();
|
||||
if (real_mirrored_klass != nullptr && real_mirrored_klass->is_instance_klass()) {
|
||||
InstanceKlass* real_mirrored_ik = InstanceKlass::cast(real_mirrored_klass);
|
||||
|
||||
ConstantPoolCache* cp_cache = real_mirrored_ik->constants()->cache();
|
||||
if (!_is_runtime_logging) {
|
||||
cp_cache = ArchiveBuilder::current()->get_buffered_addr(cp_cache);
|
||||
}
|
||||
int rr_root_index = cp_cache->archived_references_index();
|
||||
st->print(" - resolved_references: ");
|
||||
if (rr_root_index >= 0) {
|
||||
FakeOop resolved_references = _roots->at(rr_root_index);
|
||||
print_oop_info_cr(st, resolved_references);
|
||||
} else {
|
||||
st->print("null");
|
||||
}
|
||||
|
||||
st->print_cr("- ---- static fields (%d):", fake_mirror.static_oop_field_count());
|
||||
real_mirrored_ik->do_local_static_fields(&print_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // INCLUDE_CDS_JAVA_HEAP
|
||||
126
src/hotspot/share/cds/aotMapLogger.hpp
Normal file
126
src/hotspot/share/cds/aotMapLogger.hpp
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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_CDS_AOTMAPLOGGER_HPP
|
||||
#define SHARE_CDS_AOTMAPLOGGER_HPP
|
||||
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "oops/oopsHierarchy.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
class ArchiveHeapInfo;
|
||||
class DumpRegion;
|
||||
class FileMapInfo;
|
||||
class outputStream;
|
||||
|
||||
// Write detailed info to a mapfile to analyze contents of the AOT cache/CDS archive.
|
||||
// -Xlog:aot+map* can be used both when creating an AOT cache, or when using an AOT cache.
|
||||
//
|
||||
// Creating cache:
|
||||
// java -XX:AOTCacheOutput=app.aot -Xlog:aot+map*=trace -cp app.jar App
|
||||
//
|
||||
// Using cache:
|
||||
// java -XX:AOTCache=app.aot -Xlog:aot+map*=trace -cp app.jar App
|
||||
//
|
||||
// You can also print the map of a cache without executing the application by using the
|
||||
// --version flag:
|
||||
// java -XX:AOTCache=app.aot -Xlog:aot+map*=trace --version
|
||||
//
|
||||
// Because the output can be large, it's best to save it to a file
|
||||
// java -XX:AOTCache=app.aot -Xlog:aot+map*=trace:file=aot.map:none:filesize=0 --version
|
||||
class AOTMapLogger : AllStatic {
|
||||
struct ArchivedObjInfo {
|
||||
address _src_addr;
|
||||
address _buffered_addr;
|
||||
address _requested_addr;
|
||||
int _bytes;
|
||||
MetaspaceObj::Type _type;
|
||||
};
|
||||
|
||||
// FakeOop and subtypes
|
||||
class FakeOop;
|
||||
class FakeMirror;
|
||||
class FakeObjArray;
|
||||
class FakeString;
|
||||
class FakeTypeArray;
|
||||
|
||||
class RequestedMetadataAddr;
|
||||
class RuntimeGatherArchivedMetaspaceObjs;
|
||||
|
||||
static bool _is_logging_at_bootstrap;
|
||||
static bool _is_runtime_logging;
|
||||
static size_t _num_root_segments;
|
||||
static size_t _num_obj_arrays_logged;
|
||||
static GrowableArrayCHeap<FakeOop, mtClass>* _roots;
|
||||
static ArchiveHeapInfo* _dumptime_heap_info;
|
||||
|
||||
static intx _buffer_to_requested_delta;
|
||||
static intx _requested_to_mapped_metadata_delta;
|
||||
|
||||
static void runtime_log(FileMapInfo* mapinfo, GrowableArrayCHeap<ArchivedObjInfo, mtClass>* objs);
|
||||
static void runtime_log_metaspace_regions(FileMapInfo* mapinfo, GrowableArrayCHeap<ArchivedObjInfo, mtClass>* objs);
|
||||
static void dumptime_log_metaspace_region(const char* name, DumpRegion* region,
|
||||
const ArchiveBuilder::SourceObjList* src_objs);
|
||||
|
||||
// Common code for dumptime/runtime
|
||||
static void log_file_header(FileMapInfo* mapinfo);
|
||||
static void log_region_range(const char* name, address base, address top, address requested_base);
|
||||
static void log_metaspace_objects_impl(address region_base, address region_end,
|
||||
GrowableArrayCHeap<ArchivedObjInfo, mtClass>* objs, int start_idx, int end_idx);
|
||||
static void log_as_hex(address base, address top, address requested_base, bool is_heap = false);
|
||||
|
||||
// Metaspace object: type-specific logging
|
||||
static void log_constant_pool(ConstantPool* cp, address requested_addr, const char* type_name, int bytes, Thread* current);
|
||||
static void log_constant_pool_cache(ConstantPoolCache* cpc, address requested_addr,
|
||||
const char* type_name, int bytes, Thread* current);
|
||||
static void log_const_method(ConstMethod* cm, address requested_addr, const char* type_name, int bytes, Thread* current);
|
||||
static void log_klass(Klass* k, address requested_addr, const char* type_name, int bytes, Thread* current);
|
||||
static void log_method(Method* m, address requested_addr, const char* type_name, int bytes, Thread* current);
|
||||
static void log_symbol(Symbol* s, address requested_addr, const char* type_name, int bytes, Thread* current);
|
||||
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
static void dumptime_log_heap_region(ArchiveHeapInfo* heap_info);
|
||||
static void runtime_log_heap_region(FileMapInfo* mapinfo);
|
||||
|
||||
static void print_oop_info_cr(outputStream* st, FakeOop fake_oop, bool print_requested_addr = true);
|
||||
static void print_oop_details(FakeOop fake_oop, outputStream* st);
|
||||
static void log_oops(address buf_start, address buf_end);
|
||||
class ArchivedFieldPrinter; // to be replaced by ArchivedFieldPrinter2
|
||||
#endif
|
||||
|
||||
public:
|
||||
static void ergo_initialize();
|
||||
static bool is_logging_at_bootstrap() { return _is_logging_at_bootstrap; }
|
||||
|
||||
static void dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo,
|
||||
ArchiveHeapInfo* heap_info,
|
||||
char* bitmap, size_t bitmap_size_in_bytes);
|
||||
static void runtime_log(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo);
|
||||
};
|
||||
|
||||
#endif // SHARE_CDS_AOTMAPLOGGER_HPP
|
||||
@ -26,6 +26,7 @@
|
||||
#include "cds/aotClassLinker.hpp"
|
||||
#include "cds/aotLinkedClassBulkLoader.hpp"
|
||||
#include "cds/aotLogging.hpp"
|
||||
#include "cds/aotMapLogger.hpp"
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/archiveHeapWriter.hpp"
|
||||
#include "cds/archiveUtils.hpp"
|
||||
@ -46,7 +47,6 @@
|
||||
#include "interpreter/abstractInterpreter.hpp"
|
||||
#include "jvm.h"
|
||||
#include "logging/log.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "memory/memoryReserver.hpp"
|
||||
#include "memory/memRegion.hpp"
|
||||
@ -60,7 +60,6 @@
|
||||
#include "oops/oopHandle.inline.hpp"
|
||||
#include "oops/trainingData.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
#include "runtime/globals_extension.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
@ -1188,424 +1187,6 @@ void ArchiveBuilder::relocate_to_requested() {
|
||||
}
|
||||
}
|
||||
|
||||
// Write detailed info to a mapfile to analyze contents of the archive.
|
||||
// static dump:
|
||||
// java -Xshare:dump -Xlog:cds+map=trace:file=cds.map:none:filesize=0
|
||||
// dynamic dump:
|
||||
// java -cp MyApp.jar -XX:ArchiveClassesAtExit=MyApp.jsa \
|
||||
// -Xlog:cds+map=trace:file=cds.map:none:filesize=0 MyApp
|
||||
//
|
||||
// We need to do some address translation because the buffers used at dump time may be mapped to
|
||||
// a different location at runtime. At dump time, the buffers may be at arbitrary locations
|
||||
// picked by the OS. At runtime, we try to map at a fixed location (SharedBaseAddress). For
|
||||
// consistency, we log everything using runtime addresses.
|
||||
class ArchiveBuilder::CDSMapLogger : AllStatic {
|
||||
static intx buffer_to_runtime_delta() {
|
||||
// Translate the buffers used by the RW/RO regions to their eventual (requested) locations
|
||||
// at runtime.
|
||||
return ArchiveBuilder::current()->buffer_to_requested_delta();
|
||||
}
|
||||
|
||||
// rw/ro regions only
|
||||
static void log_metaspace_region(const char* name, DumpRegion* region,
|
||||
const ArchiveBuilder::SourceObjList* src_objs) {
|
||||
address region_base = address(region->base());
|
||||
address region_top = address(region->top());
|
||||
log_region(name, region_base, region_top, region_base + buffer_to_runtime_delta());
|
||||
log_metaspace_objects(region, src_objs);
|
||||
}
|
||||
|
||||
#define _LOG_PREFIX PTR_FORMAT ": @@ %-17s %d"
|
||||
|
||||
static void log_klass(Klass* k, address runtime_dest, const char* type_name, int bytes, Thread* current) {
|
||||
ResourceMark rm(current);
|
||||
log_debug(aot, map)(_LOG_PREFIX " %s",
|
||||
p2i(runtime_dest), type_name, bytes, k->external_name());
|
||||
}
|
||||
static void log_method(Method* m, address runtime_dest, const char* type_name, int bytes, Thread* current) {
|
||||
ResourceMark rm(current);
|
||||
log_debug(aot, map)(_LOG_PREFIX " %s",
|
||||
p2i(runtime_dest), type_name, bytes, m->external_name());
|
||||
}
|
||||
|
||||
// rw/ro regions only
|
||||
static void log_metaspace_objects(DumpRegion* region, const ArchiveBuilder::SourceObjList* src_objs) {
|
||||
address last_obj_base = address(region->base());
|
||||
address last_obj_end = address(region->base());
|
||||
address region_end = address(region->end());
|
||||
Thread* current = Thread::current();
|
||||
for (int i = 0; i < src_objs->objs()->length(); i++) {
|
||||
SourceObjInfo* src_info = src_objs->at(i);
|
||||
address src = src_info->source_addr();
|
||||
address dest = src_info->buffered_addr();
|
||||
log_as_hex(last_obj_base, dest, last_obj_base + buffer_to_runtime_delta());
|
||||
address runtime_dest = dest + buffer_to_runtime_delta();
|
||||
int bytes = src_info->size_in_bytes();
|
||||
|
||||
MetaspaceObj::Type type = src_info->msotype();
|
||||
const char* type_name = MetaspaceObj::type_name(type);
|
||||
|
||||
switch (type) {
|
||||
case MetaspaceObj::ClassType:
|
||||
log_klass((Klass*)src, runtime_dest, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::ConstantPoolType:
|
||||
log_klass(((ConstantPool*)src)->pool_holder(),
|
||||
runtime_dest, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::ConstantPoolCacheType:
|
||||
log_klass(((ConstantPoolCache*)src)->constant_pool()->pool_holder(),
|
||||
runtime_dest, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::MethodType:
|
||||
log_method((Method*)src, runtime_dest, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::ConstMethodType:
|
||||
log_method(((ConstMethod*)src)->method(), runtime_dest, type_name, bytes, current);
|
||||
break;
|
||||
case MetaspaceObj::SymbolType:
|
||||
{
|
||||
ResourceMark rm(current);
|
||||
Symbol* s = (Symbol*)src;
|
||||
log_debug(aot, map)(_LOG_PREFIX " %s", p2i(runtime_dest), type_name, bytes,
|
||||
s->as_quoted_ascii());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_debug(aot, map)(_LOG_PREFIX, p2i(runtime_dest), type_name, bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
last_obj_base = dest;
|
||||
last_obj_end = dest + bytes;
|
||||
}
|
||||
|
||||
log_as_hex(last_obj_base, last_obj_end, last_obj_base + buffer_to_runtime_delta());
|
||||
if (last_obj_end < region_end) {
|
||||
log_debug(aot, map)(PTR_FORMAT ": @@ Misc data %zu bytes",
|
||||
p2i(last_obj_end + buffer_to_runtime_delta()),
|
||||
size_t(region_end - last_obj_end));
|
||||
log_as_hex(last_obj_end, region_end, last_obj_end + buffer_to_runtime_delta());
|
||||
}
|
||||
}
|
||||
|
||||
#undef _LOG_PREFIX
|
||||
|
||||
// Log information about a region, whose address at dump time is [base .. top). At
|
||||
// runtime, this region will be mapped to requested_base. requested_base is nullptr if this
|
||||
// region will be mapped at os-selected addresses (such as the bitmap region), or will
|
||||
// be accessed with os::read (the header).
|
||||
//
|
||||
// Note: across -Xshare:dump runs, base may be different, but requested_base should
|
||||
// be the same as the archive contents should be deterministic.
|
||||
static void log_region(const char* name, address base, address top, address requested_base) {
|
||||
size_t size = top - base;
|
||||
base = requested_base;
|
||||
if (requested_base == nullptr) {
|
||||
top = (address)size;
|
||||
} else {
|
||||
top = requested_base + size;
|
||||
}
|
||||
log_info(aot, map)("[%-18s " PTR_FORMAT " - " PTR_FORMAT " %9zu bytes]",
|
||||
name, p2i(base), p2i(top), size);
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
static void log_heap_region(ArchiveHeapInfo* heap_info) {
|
||||
MemRegion r = heap_info->buffer_region();
|
||||
address start = address(r.start()); // start of the current oop inside the buffer
|
||||
address end = address(r.end());
|
||||
log_region("heap", start, end, ArchiveHeapWriter::buffered_addr_to_requested_addr(start));
|
||||
|
||||
LogStreamHandle(Info, aot, map) st;
|
||||
|
||||
HeapRootSegments segments = heap_info->heap_root_segments();
|
||||
assert(segments.base_offset() == 0, "Sanity");
|
||||
|
||||
for (size_t seg_idx = 0; seg_idx < segments.count(); seg_idx++) {
|
||||
address requested_start = ArchiveHeapWriter::buffered_addr_to_requested_addr(start);
|
||||
st.print_cr(PTR_FORMAT ": Heap roots segment [%d]",
|
||||
p2i(requested_start), segments.size_in_elems(seg_idx));
|
||||
start += segments.size_in_bytes(seg_idx);
|
||||
}
|
||||
log_heap_roots();
|
||||
|
||||
while (start < end) {
|
||||
size_t byte_size;
|
||||
oop source_oop = ArchiveHeapWriter::buffered_addr_to_source_obj(start);
|
||||
address requested_start = ArchiveHeapWriter::buffered_addr_to_requested_addr(start);
|
||||
st.print(PTR_FORMAT ": @@ Object ", p2i(requested_start));
|
||||
|
||||
if (source_oop != nullptr) {
|
||||
// This is a regular oop that got archived.
|
||||
// Don't print the requested addr again as we have just printed it at the beginning of the line.
|
||||
// Example:
|
||||
// 0x00000007ffd27938: @@ Object (0xfffa4f27) java.util.HashMap
|
||||
print_oop_info_cr(&st, source_oop, /*print_requested_addr=*/false);
|
||||
byte_size = source_oop->size() * BytesPerWord;
|
||||
} else if ((byte_size = ArchiveHeapWriter::get_filler_size_at(start)) > 0) {
|
||||
// We have a filler oop, which also does not exist in BufferOffsetToSourceObjectTable.
|
||||
// Example:
|
||||
// 0x00000007ffc3ffd8: @@ Object filler 40 bytes
|
||||
st.print_cr("filler %zu bytes", byte_size);
|
||||
} else {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
address oop_end = start + byte_size;
|
||||
log_as_hex(start, oop_end, requested_start, /*is_heap=*/true);
|
||||
|
||||
if (source_oop != nullptr) {
|
||||
log_oop_details(heap_info, source_oop, /*buffered_addr=*/start);
|
||||
}
|
||||
start = oop_end;
|
||||
}
|
||||
}
|
||||
|
||||
// ArchivedFieldPrinter is used to print the fields of archived objects. We can't
|
||||
// use _source_obj->print_on(), because we want to print the oop fields
|
||||
// in _source_obj with their requested addresses using print_oop_info_cr().
|
||||
class ArchivedFieldPrinter : public FieldClosure {
|
||||
ArchiveHeapInfo* _heap_info;
|
||||
outputStream* _st;
|
||||
oop _source_obj;
|
||||
address _buffered_addr;
|
||||
public:
|
||||
ArchivedFieldPrinter(ArchiveHeapInfo* heap_info, outputStream* st, oop src_obj, address buffered_addr) :
|
||||
_heap_info(heap_info), _st(st), _source_obj(src_obj), _buffered_addr(buffered_addr) {}
|
||||
|
||||
void do_field(fieldDescriptor* fd) {
|
||||
_st->print(" - ");
|
||||
BasicType ft = fd->field_type();
|
||||
switch (ft) {
|
||||
case T_ARRAY:
|
||||
case T_OBJECT:
|
||||
{
|
||||
fd->print_on(_st); // print just the name and offset
|
||||
oop obj = _source_obj->obj_field(fd->offset());
|
||||
if (java_lang_Class::is_instance(obj)) {
|
||||
obj = HeapShared::scratch_java_mirror(obj);
|
||||
}
|
||||
print_oop_info_cr(_st, obj);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (ArchiveHeapWriter::is_marked_as_native_pointer(_heap_info, _source_obj, fd->offset())) {
|
||||
print_as_native_pointer(fd);
|
||||
} else {
|
||||
fd->print_on_for(_st, cast_to_oop(_buffered_addr)); // name, offset, value
|
||||
_st->cr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_as_native_pointer(fieldDescriptor* fd) {
|
||||
LP64_ONLY(assert(fd->field_type() == T_LONG, "must be"));
|
||||
NOT_LP64 (assert(fd->field_type() == T_INT, "must be"));
|
||||
|
||||
// We have a field that looks like an integer, but it's actually a pointer to a MetaspaceObj.
|
||||
address source_native_ptr = (address)
|
||||
LP64_ONLY(_source_obj->long_field(fd->offset()))
|
||||
NOT_LP64( _source_obj->int_field (fd->offset()));
|
||||
ArchiveBuilder* builder = ArchiveBuilder::current();
|
||||
|
||||
// The value of the native pointer at runtime.
|
||||
address requested_native_ptr = builder->to_requested(builder->get_buffered_addr(source_native_ptr));
|
||||
|
||||
// The address of _source_obj at runtime
|
||||
oop requested_obj = ArchiveHeapWriter::source_obj_to_requested_obj(_source_obj);
|
||||
// The address of this field in the requested space
|
||||
assert(requested_obj != nullptr, "Attempting to load field from null oop");
|
||||
address requested_field_addr = cast_from_oop<address>(requested_obj) + fd->offset();
|
||||
|
||||
fd->print_on(_st);
|
||||
_st->print_cr(PTR_FORMAT " (marked metadata pointer @" PTR_FORMAT " )",
|
||||
p2i(requested_native_ptr), p2i(requested_field_addr));
|
||||
}
|
||||
};
|
||||
|
||||
// Print the fields of instanceOops, or the elements of arrayOops
|
||||
static void log_oop_details(ArchiveHeapInfo* heap_info, oop source_oop, address buffered_addr) {
|
||||
LogStreamHandle(Trace, aot, map, oops) st;
|
||||
if (st.is_enabled()) {
|
||||
Klass* source_klass = source_oop->klass();
|
||||
ArchiveBuilder* builder = ArchiveBuilder::current();
|
||||
Klass* requested_klass = builder->to_requested(builder->get_buffered_addr(source_klass));
|
||||
|
||||
st.print(" - klass: ");
|
||||
source_klass->print_value_on(&st);
|
||||
st.print(" " PTR_FORMAT, p2i(requested_klass));
|
||||
st.cr();
|
||||
|
||||
if (source_oop->is_typeArray()) {
|
||||
TypeArrayKlass::cast(source_klass)->oop_print_elements_on(typeArrayOop(source_oop), &st);
|
||||
} else if (source_oop->is_objArray()) {
|
||||
objArrayOop source_obj_array = objArrayOop(source_oop);
|
||||
for (int i = 0; i < source_obj_array->length(); i++) {
|
||||
st.print(" -%4d: ", i);
|
||||
oop obj = source_obj_array->obj_at(i);
|
||||
if (java_lang_Class::is_instance(obj)) {
|
||||
obj = HeapShared::scratch_java_mirror(obj);
|
||||
}
|
||||
print_oop_info_cr(&st, obj);
|
||||
}
|
||||
} else {
|
||||
st.print_cr(" - fields (%zu words):", source_oop->size());
|
||||
ArchivedFieldPrinter print_field(heap_info, &st, source_oop, buffered_addr);
|
||||
InstanceKlass::cast(source_klass)->print_nonstatic_fields(&print_field);
|
||||
|
||||
if (java_lang_Class::is_instance(source_oop)) {
|
||||
oop scratch_mirror = source_oop;
|
||||
st.print(" - signature: ");
|
||||
print_class_signature_for_mirror(&st, scratch_mirror);
|
||||
st.cr();
|
||||
|
||||
Klass* src_klass = java_lang_Class::as_Klass(scratch_mirror);
|
||||
if (src_klass != nullptr && src_klass->is_instance_klass()) {
|
||||
oop rr = HeapShared::scratch_resolved_references(InstanceKlass::cast(src_klass)->constants());
|
||||
st.print(" - archived_resolved_references: ");
|
||||
print_oop_info_cr(&st, rr);
|
||||
|
||||
// We need to print the fields in the scratch_mirror, not the original mirror.
|
||||
// (if a class is not aot-initialized, static fields in its scratch mirror will be cleared).
|
||||
assert(scratch_mirror == HeapShared::scratch_java_mirror(src_klass->java_mirror()), "sanity");
|
||||
st.print_cr("- ---- static fields (%d):", java_lang_Class::static_oop_field_count(scratch_mirror));
|
||||
InstanceKlass::cast(src_klass)->do_local_static_fields(&print_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_class_signature_for_mirror(outputStream* st, oop scratch_mirror) {
|
||||
assert(java_lang_Class::is_instance(scratch_mirror), "sanity");
|
||||
if (java_lang_Class::is_primitive(scratch_mirror)) {
|
||||
for (int i = T_BOOLEAN; i < T_VOID+1; i++) {
|
||||
BasicType bt = (BasicType)i;
|
||||
if (!is_reference_type(bt) && scratch_mirror == HeapShared::scratch_java_mirror(bt)) {
|
||||
oop orig_mirror = Universe::java_mirror(bt);
|
||||
java_lang_Class::print_signature(orig_mirror, st);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
java_lang_Class::print_signature(scratch_mirror, st);
|
||||
}
|
||||
|
||||
static void log_heap_roots() {
|
||||
LogStreamHandle(Trace, aot, map, oops) st;
|
||||
if (st.is_enabled()) {
|
||||
for (int i = 0; i < HeapShared::pending_roots()->length(); i++) {
|
||||
st.print("roots[%4d]: ", i);
|
||||
print_oop_info_cr(&st, HeapShared::pending_roots()->at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Example output:
|
||||
// - The first number is the requested address (if print_requested_addr == true)
|
||||
// - The second number is the narrowOop version of the requested address (if UseCompressedOops == true)
|
||||
// 0x00000007ffc7e840 (0xfff8fd08) java.lang.Class Ljava/util/Array;
|
||||
// 0x00000007ffc000f8 (0xfff8001f) [B length: 11
|
||||
static void print_oop_info_cr(outputStream* st, oop source_oop, bool print_requested_addr = true) {
|
||||
if (source_oop == nullptr) {
|
||||
st->print_cr("null");
|
||||
} else {
|
||||
ResourceMark rm;
|
||||
oop requested_obj = ArchiveHeapWriter::source_obj_to_requested_obj(source_oop);
|
||||
if (print_requested_addr) {
|
||||
st->print(PTR_FORMAT " ", p2i(requested_obj));
|
||||
}
|
||||
if (UseCompressedOops) {
|
||||
st->print("(0x%08x) ", CompressedOops::narrow_oop_value(requested_obj));
|
||||
}
|
||||
if (source_oop->is_array()) {
|
||||
int array_len = arrayOop(source_oop)->length();
|
||||
st->print_cr("%s length: %d", source_oop->klass()->external_name(), array_len);
|
||||
} else {
|
||||
st->print("%s", source_oop->klass()->external_name());
|
||||
|
||||
if (java_lang_String::is_instance(source_oop)) {
|
||||
st->print(" ");
|
||||
java_lang_String::print(source_oop, st);
|
||||
} else if (java_lang_Class::is_instance(source_oop)) {
|
||||
oop scratch_mirror = source_oop;
|
||||
|
||||
st->print(" ");
|
||||
print_class_signature_for_mirror(st, scratch_mirror);
|
||||
|
||||
Klass* src_klass = java_lang_Class::as_Klass(scratch_mirror);
|
||||
if (src_klass != nullptr && src_klass->is_instance_klass()) {
|
||||
InstanceKlass* buffered_klass =
|
||||
ArchiveBuilder::current()->get_buffered_addr(InstanceKlass::cast(src_klass));
|
||||
if (buffered_klass->has_aot_initialized_mirror()) {
|
||||
st->print(" (aot-inited)");
|
||||
}
|
||||
}
|
||||
}
|
||||
st->cr();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // INCLUDE_CDS_JAVA_HEAP
|
||||
|
||||
// Log all the data [base...top). Pretend that the base address
|
||||
// will be mapped to requested_base at run-time.
|
||||
static void log_as_hex(address base, address top, address requested_base, bool is_heap = false) {
|
||||
assert(top >= base, "must be");
|
||||
|
||||
LogStreamHandle(Trace, aot, map) lsh;
|
||||
if (lsh.is_enabled()) {
|
||||
int unitsize = sizeof(address);
|
||||
if (is_heap && UseCompressedOops) {
|
||||
// This makes the compressed oop pointers easier to read, but
|
||||
// longs and doubles will be split into two words.
|
||||
unitsize = sizeof(narrowOop);
|
||||
}
|
||||
os::print_hex_dump(&lsh, base, top, unitsize, /* print_ascii=*/true, /* bytes_per_line=*/32, requested_base);
|
||||
}
|
||||
}
|
||||
|
||||
static void log_header(FileMapInfo* mapinfo) {
|
||||
LogStreamHandle(Info, aot, map) lsh;
|
||||
if (lsh.is_enabled()) {
|
||||
mapinfo->print(&lsh);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static void log(ArchiveBuilder* builder, FileMapInfo* mapinfo,
|
||||
ArchiveHeapInfo* heap_info,
|
||||
char* bitmap, size_t bitmap_size_in_bytes) {
|
||||
log_info(aot, map)("%s CDS archive map for %s", CDSConfig::is_dumping_static_archive() ? "Static" : "Dynamic", mapinfo->full_path());
|
||||
|
||||
address header = address(mapinfo->header());
|
||||
address header_end = header + mapinfo->header()->header_size();
|
||||
log_region("header", header, header_end, nullptr);
|
||||
log_header(mapinfo);
|
||||
log_as_hex(header, header_end, nullptr);
|
||||
|
||||
DumpRegion* rw_region = &builder->_rw_region;
|
||||
DumpRegion* ro_region = &builder->_ro_region;
|
||||
|
||||
log_metaspace_region("rw region", rw_region, &builder->_rw_src_objs);
|
||||
log_metaspace_region("ro region", ro_region, &builder->_ro_src_objs);
|
||||
|
||||
address bitmap_end = address(bitmap + bitmap_size_in_bytes);
|
||||
log_region("bitmap", address(bitmap), bitmap_end, nullptr);
|
||||
log_as_hex((address)bitmap, bitmap_end, nullptr);
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
if (heap_info->is_used()) {
|
||||
log_heap_region(heap_info);
|
||||
}
|
||||
#endif
|
||||
|
||||
log_info(aot, map)("[End of CDS archive map]");
|
||||
}
|
||||
}; // end ArchiveBuilder::CDSMapLogger
|
||||
|
||||
void ArchiveBuilder::print_stats() {
|
||||
_alloc_stats.print_stats(int(_ro_region.used()), int(_rw_region.used()));
|
||||
}
|
||||
@ -1645,8 +1226,7 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, ArchiveHeapInfo* heap_i
|
||||
}
|
||||
|
||||
if (log_is_enabled(Info, aot, map)) {
|
||||
CDSMapLogger::log(this, mapinfo, heap_info,
|
||||
bitmap, bitmap_size_in_bytes);
|
||||
AOTMapLogger::dumptime_log(this, mapinfo, heap_info, bitmap, bitmap_size_in_bytes);
|
||||
}
|
||||
CDS_JAVA_HEAP_ONLY(HeapShared::destroy_archived_object_cache());
|
||||
FREE_C_HEAP_ARRAY(char, bitmap);
|
||||
|
||||
@ -93,6 +93,8 @@ constexpr size_t SharedSpaceObjectAlignment = Metaspace::min_allocation_alignmen
|
||||
// buffered_address + _buffer_to_requested_delta == requested_address
|
||||
//
|
||||
class ArchiveBuilder : public StackObj {
|
||||
friend class AOTMapLogger;
|
||||
|
||||
protected:
|
||||
DumpRegion* _current_dump_region;
|
||||
address _buffer_bottom; // for writing the contents of rw/ro regions
|
||||
@ -202,8 +204,6 @@ private:
|
||||
SourceObjInfo* at(int i) const { return objs()->at(i); }
|
||||
};
|
||||
|
||||
class CDSMapLogger;
|
||||
|
||||
static const int INITIAL_TABLE_SIZE = 15889;
|
||||
static const int MAX_TABLE_SIZE = 1000000;
|
||||
|
||||
@ -316,6 +316,12 @@ public:
|
||||
return (T)(address(obj) + _buffer_to_requested_delta);
|
||||
}
|
||||
|
||||
template <typename T> T requested_to_buffered(T obj) const {
|
||||
T b = (T)(address(obj) - _buffer_to_requested_delta);
|
||||
assert(is_in_buffer_space(b), "must be");
|
||||
return b;
|
||||
}
|
||||
|
||||
static intx get_buffer_to_requested_delta() {
|
||||
return current()->buffer_to_requested_delta();
|
||||
}
|
||||
|
||||
@ -162,6 +162,44 @@ oop ArchiveHeapWriter::buffered_addr_to_source_obj(address buffered_addr) {
|
||||
}
|
||||
}
|
||||
|
||||
Klass* ArchiveHeapWriter::real_klass_of_buffered_oop(address buffered_addr) {
|
||||
oop p = buffered_addr_to_source_obj(buffered_addr);
|
||||
if (p != nullptr) {
|
||||
return p->klass();
|
||||
} else if (get_filler_size_at(buffered_addr) > 0) {
|
||||
return Universe::fillerArrayKlass();
|
||||
} else {
|
||||
// This is one of the root segments
|
||||
return Universe::objectArrayKlass();
|
||||
}
|
||||
}
|
||||
|
||||
size_t ArchiveHeapWriter::size_of_buffered_oop(address buffered_addr) {
|
||||
oop p = buffered_addr_to_source_obj(buffered_addr);
|
||||
if (p != nullptr) {
|
||||
return p->size();
|
||||
}
|
||||
|
||||
size_t nbytes = get_filler_size_at(buffered_addr);
|
||||
if (nbytes > 0) {
|
||||
assert((nbytes % BytesPerWord) == 0, "should be aligned");
|
||||
return nbytes / BytesPerWord;
|
||||
}
|
||||
|
||||
address hrs = buffer_bottom();
|
||||
for (size_t seg_idx = 0; seg_idx < _heap_root_segments.count(); seg_idx++) {
|
||||
nbytes = _heap_root_segments.size_in_bytes(seg_idx);
|
||||
if (hrs == buffered_addr) {
|
||||
assert((nbytes % BytesPerWord) == 0, "should be aligned");
|
||||
return nbytes / BytesPerWord;
|
||||
}
|
||||
hrs += nbytes;
|
||||
}
|
||||
|
||||
ShouldNotReachHere();
|
||||
return 0;
|
||||
}
|
||||
|
||||
address ArchiveHeapWriter::buffered_addr_to_requested_addr(address buffered_addr) {
|
||||
return _requested_bottom + buffered_address_to_offset(buffered_addr);
|
||||
}
|
||||
@ -709,27 +747,6 @@ void ArchiveHeapWriter::mark_native_pointer(oop src_obj, int field_offset) {
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a jlong/jint field that's actually a pointer to a MetaspaceObj?
|
||||
bool ArchiveHeapWriter::is_marked_as_native_pointer(ArchiveHeapInfo* heap_info, oop src_obj, int field_offset) {
|
||||
HeapShared::CachedOopInfo* p = HeapShared::archived_object_cache()->get(src_obj);
|
||||
assert(p != nullptr, "must be");
|
||||
|
||||
// requested_field_addr = the address of this field in the requested space
|
||||
oop requested_obj = requested_obj_from_buffer_offset(p->buffer_offset());
|
||||
Metadata** requested_field_addr = (Metadata**)(cast_from_oop<address>(requested_obj) + field_offset);
|
||||
assert((Metadata**)_requested_bottom <= requested_field_addr && requested_field_addr < (Metadata**) _requested_top, "range check");
|
||||
|
||||
BitMap::idx_t idx = requested_field_addr - (Metadata**) _requested_bottom;
|
||||
// Leading zeros have been removed so some addresses may not be in the ptrmap
|
||||
size_t start_pos = FileMapInfo::current_info()->heap_ptrmap_start_pos();
|
||||
if (idx < start_pos) {
|
||||
return false;
|
||||
} else {
|
||||
idx -= start_pos;
|
||||
}
|
||||
return (idx < heap_info->ptrmap()->size()) && (heap_info->ptrmap()->at(idx) == true);
|
||||
}
|
||||
|
||||
void ArchiveHeapWriter::compute_ptrmap(ArchiveHeapInfo* heap_info) {
|
||||
int num_non_null_ptrs = 0;
|
||||
Metadata** bottom = (Metadata**) _requested_bottom;
|
||||
|
||||
@ -236,11 +236,11 @@ public:
|
||||
static size_t get_filler_size_at(address buffered_addr);
|
||||
|
||||
static void mark_native_pointer(oop src_obj, int offset);
|
||||
static bool is_marked_as_native_pointer(ArchiveHeapInfo* heap_info, oop src_obj, int field_offset);
|
||||
static oop source_obj_to_requested_obj(oop src_obj);
|
||||
static oop buffered_addr_to_source_obj(address buffered_addr);
|
||||
static address buffered_addr_to_requested_addr(address buffered_addr);
|
||||
|
||||
static Klass* real_klass_of_buffered_oop(address buffered_addr);
|
||||
static size_t size_of_buffered_oop(address buffered_addr);
|
||||
};
|
||||
#endif // INCLUDE_CDS_JAVA_HEAP
|
||||
#endif // SHARE_CDS_ARCHIVEHEAPWRITER_HPP
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "cds/aotLogging.hpp"
|
||||
#include "cds/aotMapLogger.hpp"
|
||||
#include "cds/archiveHeapLoader.hpp"
|
||||
#include "cds/cdsConfig.hpp"
|
||||
#include "cds/classListWriter.hpp"
|
||||
@ -104,6 +105,8 @@ void CDSConfig::ergo_initialize() {
|
||||
if (!is_dumping_heap()) {
|
||||
_is_dumping_full_module_graph = false;
|
||||
}
|
||||
|
||||
AOTMapLogger::ergo_initialize();
|
||||
}
|
||||
|
||||
const char* CDSConfig::default_archive_path() {
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "cds/aotConstantPoolResolver.hpp"
|
||||
#include "cds/aotLinkedClassBulkLoader.hpp"
|
||||
#include "cds/aotLogging.hpp"
|
||||
#include "cds/aotMapLogger.hpp"
|
||||
#include "cds/aotReferenceObjSupport.hpp"
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/archiveHeapLoader.hpp"
|
||||
@ -325,6 +326,24 @@ void MetaspaceShared::initialize_for_static_dump() {
|
||||
// Called by universe_post_init()
|
||||
void MetaspaceShared::post_initialize(TRAPS) {
|
||||
if (CDSConfig::is_using_archive()) {
|
||||
FileMapInfo *static_mapinfo = FileMapInfo::current_info();
|
||||
FileMapInfo *dynamic_mapinfo = FileMapInfo::dynamic_info();
|
||||
|
||||
if (AOTMapLogger::is_logging_at_bootstrap()) {
|
||||
// The map logging needs to be done here, as it requires some stubs on Windows,
|
||||
// which are not generated until the end of init_globals().
|
||||
AOTMapLogger::runtime_log(static_mapinfo, dynamic_mapinfo);
|
||||
}
|
||||
|
||||
// Close any open file descriptors. However, mmap'ed pages will remain in memory.
|
||||
static_mapinfo->close();
|
||||
static_mapinfo->unmap_region(MetaspaceShared::bm);
|
||||
|
||||
if (dynamic_mapinfo != nullptr) {
|
||||
dynamic_mapinfo->close();
|
||||
dynamic_mapinfo->unmap_region(MetaspaceShared::bm);
|
||||
}
|
||||
|
||||
int size = AOTClassLocationConfig::runtime()->length();
|
||||
if (size > 0) {
|
||||
CDSProtectionDomain::allocate_shared_data_arrays(size, CHECK);
|
||||
@ -1954,6 +1973,7 @@ class CountSharedSymbols : public SymbolClosure {
|
||||
|
||||
void MetaspaceShared::initialize_shared_spaces() {
|
||||
FileMapInfo *static_mapinfo = FileMapInfo::current_info();
|
||||
FileMapInfo *dynamic_mapinfo = FileMapInfo::dynamic_info();
|
||||
|
||||
// Verify various attributes of the archive, plus initialize the
|
||||
// shared string/symbol tables.
|
||||
@ -1969,19 +1989,11 @@ void MetaspaceShared::initialize_shared_spaces() {
|
||||
Universe::load_archived_object_instances();
|
||||
AOTCodeCache::initialize();
|
||||
|
||||
// Close the mapinfo file
|
||||
static_mapinfo->close();
|
||||
|
||||
static_mapinfo->unmap_region(MetaspaceShared::bm);
|
||||
|
||||
FileMapInfo *dynamic_mapinfo = FileMapInfo::dynamic_info();
|
||||
if (dynamic_mapinfo != nullptr) {
|
||||
intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data();
|
||||
ReadClosure rc(&buffer, (intptr_t)SharedBaseAddress);
|
||||
ArchiveBuilder::serialize_dynamic_archivable_items(&rc);
|
||||
DynamicArchive::setup_array_klasses();
|
||||
dynamic_mapinfo->close();
|
||||
dynamic_mapinfo->unmap_region(MetaspaceShared::bm);
|
||||
}
|
||||
|
||||
LogStreamHandle(Info, aot) lsh;
|
||||
|
||||
@ -1179,6 +1179,12 @@ const char* SystemDictionaryShared::loader_type_for_shared_class(Klass* k) {
|
||||
}
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::get_all_archived_classes(bool is_static_archive, GrowableArray<Klass*>* classes) {
|
||||
get_archive(is_static_archive)->_builtin_dictionary.iterate([&] (const RunTimeClassInfo* record) {
|
||||
classes->append(record->klass());
|
||||
});
|
||||
}
|
||||
|
||||
class SharedDictionaryPrinter : StackObj {
|
||||
outputStream* _st;
|
||||
int _index;
|
||||
|
||||
@ -115,6 +115,8 @@ class DumpTimeSharedClassTable;
|
||||
class RunTimeClassInfo;
|
||||
class RunTimeSharedDictionary;
|
||||
|
||||
template <typename E> class GrowableArray;
|
||||
|
||||
class SharedClassLoadingMark {
|
||||
private:
|
||||
Thread* THREAD;
|
||||
@ -269,6 +271,7 @@ public:
|
||||
bool is_static_archive = true);
|
||||
static void serialize_vm_classes(class SerializeClosure* soc);
|
||||
static const char* loader_type_for_shared_class(Klass* k);
|
||||
static void get_all_archived_classes(bool is_static_archive, GrowableArray<Klass*>* classes);
|
||||
static void print() { return print_on(tty); }
|
||||
static void print_on(outputStream* st) NOT_CDS_RETURN;
|
||||
static void print_shared_archive(outputStream* st, bool is_static = true) NOT_CDS_RETURN;
|
||||
|
||||
@ -111,6 +111,7 @@ class ConstantPoolCache: public MetaspaceObj {
|
||||
|
||||
oop archived_references() NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
|
||||
void clear_archived_references() NOT_CDS_JAVA_HEAP_RETURN;
|
||||
CDS_JAVA_HEAP_ONLY(int archived_references_index() { return _archived_references_index; })
|
||||
|
||||
inline objArrayOop resolved_references();
|
||||
void set_resolved_references(OopHandle s) { _resolved_references = s; }
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2025, 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
|
||||
@ -44,6 +44,7 @@ class objArrayOopDesc : public arrayOopDesc {
|
||||
friend class Continuation;
|
||||
template <typename T>
|
||||
friend class RawOopWriter;
|
||||
friend class AOTMapLogger;
|
||||
|
||||
template <class T> T* obj_at_addr(int index) const;
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
* @test
|
||||
* @bug 8308903
|
||||
* @summary Test the contents of -Xlog:aot+map
|
||||
* @requires vm.flagless
|
||||
* @requires vm.cds
|
||||
* @library /test/lib
|
||||
* @run driver CDSMapTest
|
||||
@ -33,6 +34,8 @@
|
||||
import jdk.test.lib.cds.CDSOptions;
|
||||
import jdk.test.lib.cds.CDSTestUtils;
|
||||
import jdk.test.lib.Platform;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CDSMapTest {
|
||||
@ -46,34 +49,57 @@ public class CDSMapTest {
|
||||
}
|
||||
|
||||
public static void doTest(boolean compressed) throws Exception {
|
||||
ArrayList<String> dumpArgs = new ArrayList<>();
|
||||
ArrayList<String> vmArgs = new ArrayList<>();
|
||||
|
||||
// Use the same heap size as make/Images.gmk
|
||||
dumpArgs.add("-Xmx128M");
|
||||
vmArgs.add("-Xmx128M");
|
||||
|
||||
if (Platform.is64bit()) {
|
||||
// These options are available only on 64-bit.
|
||||
String sign = (compressed) ? "+" : "-";
|
||||
dumpArgs.add("-XX:" + sign + "UseCompressedOops");
|
||||
vmArgs.add("-XX:" + sign + "UseCompressedOops");
|
||||
}
|
||||
|
||||
dump(dumpArgs);
|
||||
String archiveFile = dump(vmArgs);
|
||||
exec(vmArgs, archiveFile);
|
||||
|
||||
}
|
||||
|
||||
static int id = 0;
|
||||
static void dump(ArrayList<String> args, String... more) throws Exception {
|
||||
|
||||
// Create a map file when creating the archive
|
||||
static String dump(ArrayList<String> args) throws Exception {
|
||||
String logName = "SharedArchiveFile" + (id++);
|
||||
String archiveName = logName + ".jsa";
|
||||
String mapName = logName + ".map";
|
||||
CDSOptions opts = (new CDSOptions())
|
||||
.addPrefix("-Xlog:cds=debug")
|
||||
// filesize=0 ensures that a large map file not broken up in multiple files.
|
||||
.addPrefix("-Xlog:aot+map=debug,aot+map+oops=trace:file=" + mapName + ":none:filesize=0")
|
||||
.setArchiveName(archiveName)
|
||||
.addSuffix(args)
|
||||
.addSuffix(more);
|
||||
.addSuffix(args);
|
||||
CDSTestUtils.createArchiveAndCheck(opts);
|
||||
|
||||
CDSMapReader.MapFile mapFile = CDSMapReader.read(mapName);
|
||||
CDSMapReader.validate(mapFile);
|
||||
|
||||
return archiveName;
|
||||
}
|
||||
|
||||
// Create a map file when using the archive
|
||||
static void exec(ArrayList<String> vmArgs, String archiveFile) throws Exception {
|
||||
String mapName = archiveFile + ".exec.map";
|
||||
vmArgs.add("-XX:SharedArchiveFile=" + archiveFile);
|
||||
vmArgs.add("-Xlog:cds=debug");
|
||||
vmArgs.add("-Xshare:on");
|
||||
vmArgs.add("-Xlog:aot+map=debug,aot+map+oops=trace:file=" + mapName + ":none:filesize=0");
|
||||
vmArgs.add("--version");
|
||||
String[] cmdLine = vmArgs.toArray(new String[vmArgs.size()]);
|
||||
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmdLine);
|
||||
OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "exec");
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
CDSMapReader.MapFile mapFile = CDSMapReader.read(mapName);
|
||||
CDSMapReader.validate(mapFile);
|
||||
}
|
||||
}
|
||||
|
||||
139
test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java
Normal file
139
test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=aot
|
||||
* @bug 8362566
|
||||
* @summary Test the contents of -Xlog:aot+map with AOT workflow
|
||||
* @requires vm.flagless
|
||||
* @requires vm.cds.supports.aot.class.linking
|
||||
* @library /test/lib /test/hotspot/jtreg/runtime/cds
|
||||
* @build AOTMapTest
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp
|
||||
* @run driver AOTMapTest AOT --two-step-training
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=dynamic
|
||||
* @bug 8362566
|
||||
* @summary Test the contents of -Xlog:aot+map with AOT workflow
|
||||
* @requires vm.flagless
|
||||
* @requires vm.cds.supports.aot.class.linking
|
||||
* @library /test/lib /test/hotspot/jtreg/runtime/cds
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @build AOTMapTest
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. AOTMapTest DYNAMIC
|
||||
*/
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import jdk.test.lib.cds.CDSAppTester;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
import jdk.test.lib.Platform;
|
||||
|
||||
public class AOTMapTest {
|
||||
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
|
||||
static final String mainClass = "AOTMapTestApp";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
doTest(args, false);
|
||||
|
||||
if (Platform.is64bit()) {
|
||||
// There's no oop/klass compression on 32-bit.
|
||||
doTest(args, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void doTest(String[] args, boolean compressed) throws Exception {
|
||||
Tester tester = new Tester(compressed);
|
||||
tester.run(args);
|
||||
|
||||
validate(tester.dumpMapFile);
|
||||
validate(tester.runMapFile);
|
||||
}
|
||||
|
||||
static void validate(String mapFileName) {
|
||||
CDSMapReader.MapFile mapFile = CDSMapReader.read(mapFileName);
|
||||
CDSMapReader.validate(mapFile);
|
||||
}
|
||||
|
||||
static class Tester extends CDSAppTester {
|
||||
boolean compressed;
|
||||
String dumpMapFile;
|
||||
String runMapFile;
|
||||
|
||||
public Tester(boolean compressed) {
|
||||
super(mainClass);
|
||||
this.compressed = compressed;
|
||||
|
||||
dumpMapFile = "test" + (compressed ? "0" : "1") + ".dump.aotmap";
|
||||
runMapFile = "test" + (compressed ? "0" : "1") + ".run.aotmap";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String classpath(RunMode runMode) {
|
||||
return appJar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] vmArgs(RunMode runMode) {
|
||||
ArrayList<String> vmArgs = new ArrayList<>();
|
||||
|
||||
vmArgs.add("-Xmx128M");
|
||||
vmArgs.add("-Xlog:aot=debug");
|
||||
|
||||
if (Platform.is64bit()) {
|
||||
// These options are available only on 64-bit.
|
||||
String sign = (compressed) ? "+" : "-";
|
||||
vmArgs.add("-XX:" + sign + "UseCompressedOops");
|
||||
}
|
||||
|
||||
// filesize=0 ensures that a large map file not broken up in multiple files.
|
||||
String logMapPrefix = "-Xlog:aot+map=debug,aot+map+oops=trace:file=";
|
||||
String logMapSuffix = ":none:filesize=0";
|
||||
|
||||
if (runMode == RunMode.ASSEMBLY || runMode == RunMode.DUMP_DYNAMIC) {
|
||||
vmArgs.add(logMapPrefix + dumpMapFile + logMapSuffix);
|
||||
} else if (runMode == RunMode.PRODUCTION) {
|
||||
vmArgs.add(logMapPrefix + runMapFile + logMapSuffix);
|
||||
}
|
||||
|
||||
return vmArgs.toArray(new String[vmArgs.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] appCommandLine(RunMode runMode) {
|
||||
return new String[] {
|
||||
mainClass,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AOTMapTestApp {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello AOTMapTestApp");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user