Skip to content

Commit

Permalink
8283474: Include detailed heap object info in CDS map file
Browse files Browse the repository at this point in the history
Reviewed-by: ccheung, stuefe
  • Loading branch information
iklam committed Apr 2, 2022
1 parent 060a188 commit c1e67b6
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 46 deletions.
120 changes: 79 additions & 41 deletions src/hotspot/share/cds/archiveBuilder.cpp
Expand Up @@ -27,6 +27,7 @@
#include "cds/archiveUtils.hpp"
#include "cds/cppVtables.hpp"
#include "cds/dumpAllocStats.hpp"
#include "cds/heapShared.hpp"
#include "cds/metaspaceShared.hpp"
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/symbolTable.hpp"
Expand All @@ -40,6 +41,7 @@
#include "memory/resourceArea.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oopHandle.inline.hpp"
#include "runtime/arguments.hpp"
#include "runtime/globals_extension.hpp"
Expand Down Expand Up @@ -929,27 +931,29 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {
}

// rw/ro regions only
static void write_dump_region(const char* name, DumpRegion* region) {
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());
write_region(name, region_base, region_top, region_base + buffer_to_runtime_delta());
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 write_klass(Klass* k, address runtime_dest, const char* type_name, int bytes, Thread* current) {
static void log_klass(Klass* k, address runtime_dest, const char* type_name, int bytes, Thread* current) {
ResourceMark rm(current);
log_debug(cds, map)(_LOG_PREFIX " %s",
p2i(runtime_dest), type_name, bytes, k->external_name());
}
static void write_method(Method* m, address runtime_dest, const char* type_name, int bytes, Thread* current) {
static void log_method(Method* m, address runtime_dest, const char* type_name, int bytes, Thread* current) {
ResourceMark rm(current);
log_debug(cds, map)(_LOG_PREFIX " %s",
p2i(runtime_dest), type_name, bytes, m->external_name());
}

// rw/ro regions only
static void write_objects(DumpRegion* region, const ArchiveBuilder::SourceObjList* src_objs) {
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());
Expand All @@ -958,7 +962,7 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {
SourceObjInfo* src_info = src_objs->at(i);
address src = src_info->orig_obj();
address dest = src_info->dumped_addr();
write_data(last_obj_base, dest, last_obj_base + buffer_to_runtime_delta());
log_data(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();

Expand All @@ -967,21 +971,21 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {

switch (type) {
case MetaspaceObj::ClassType:
write_klass((Klass*)src, runtime_dest, type_name, bytes, current);
log_klass((Klass*)src, runtime_dest, type_name, bytes, current);
break;
case MetaspaceObj::ConstantPoolType:
write_klass(((ConstantPool*)src)->pool_holder(),
log_klass(((ConstantPool*)src)->pool_holder(),
runtime_dest, type_name, bytes, current);
break;
case MetaspaceObj::ConstantPoolCacheType:
write_klass(((ConstantPoolCache*)src)->constant_pool()->pool_holder(),
log_klass(((ConstantPoolCache*)src)->constant_pool()->pool_holder(),
runtime_dest, type_name, bytes, current);
break;
case MetaspaceObj::MethodType:
write_method((Method*)src, runtime_dest, type_name, bytes, current);
log_method((Method*)src, runtime_dest, type_name, bytes, current);
break;
case MetaspaceObj::ConstMethodType:
write_method(((ConstMethod*)src)->method(), runtime_dest, type_name, bytes, current);
log_method(((ConstMethod*)src)->method(), runtime_dest, type_name, bytes, current);
break;
case MetaspaceObj::SymbolType:
{
Expand All @@ -1000,22 +1004,22 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {
last_obj_end = dest + bytes;
}

write_data(last_obj_base, last_obj_end, last_obj_base + buffer_to_runtime_delta());
log_data(last_obj_base, last_obj_end, last_obj_base + buffer_to_runtime_delta());
if (last_obj_end < region_end) {
log_debug(cds, map)(PTR_FORMAT ": @@ Misc data " SIZE_FORMAT " bytes",
p2i(last_obj_end + buffer_to_runtime_delta()),
size_t(region_end - last_obj_end));
write_data(last_obj_end, region_end, last_obj_end + buffer_to_runtime_delta());
log_data(last_obj_end, region_end, last_obj_end + buffer_to_runtime_delta());
}
}

#undef _LOG_PREFIX

// Write information about a region, whose address at dump time is [base .. top). At
// Log information about a region, whose address at dump time is [base .. top). At
// runtime, this region will be mapped to runtime_base. runtime_base is 0 if this
// region will be mapped at os-selected addresses (such as the bitmap region), or will
// be accessed with os::read (the header).
static void write_region(const char* name, address base, address top, address runtime_base) {
static void log_region(const char* name, address base, address top, address runtime_base) {
size_t size = top - base;
base = runtime_base;
top = runtime_base + size;
Expand All @@ -1024,69 +1028,102 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {
}

// open and closed archive regions
static void write_heap_region(const char* which, GrowableArray<MemRegion> *regions) {
static void log_heap_regions(const char* which, GrowableArray<MemRegion> *regions) {
#if INCLUDE_CDS_JAVA_HEAP
for (int i = 0; i < regions->length(); i++) {
address start = address(regions->at(i).start());
address end = address(regions->at(i).end());
write_region(which, start, end, start);
write_data(start, end, start);
log_region(which, start, end, start);

while (start < end) {
size_t byte_size;
oop archived_oop = cast_to_oop(start);
oop original_oop = HeapShared::get_original_object(archived_oop);
if (original_oop != NULL) {
ResourceMark rm;
log_info(cds, map)(PTR_FORMAT ": @@ Object %s",
p2i(start), original_oop->klass()->external_name());
byte_size = original_oop->size() * BytesPerWord;
} else if (archived_oop == HeapShared::roots()) {
// HeapShared::roots() is copied specially so it doesn't exist in
// HeapShared::OriginalObjectTable. See HeapShared::copy_roots().
log_info(cds, map)(PTR_FORMAT ": @@ Object HeapShared:roots (ObjArray)",
p2i(start));
byte_size = objArrayOopDesc::object_size(HeapShared::roots()->length()) * BytesPerWord;
} else {
// We have reached the end of the region
break;
}
address oop_end = start + byte_size;
log_data(start, oop_end, start, /*is_heap=*/true);
start = oop_end;
}
if (start < end) {
log_info(cds, map)(PTR_FORMAT ": @@ Unused heap space " SIZE_FORMAT " bytes",
p2i(start), size_t(end - start));
log_data(start, end, start, /*is_heap=*/true);
}
}
#endif
}

// Dump all the data [base...top). Pretend that the base address
// Log all the data [base...top). Pretend that the base address
// will be mapped to runtime_base at run-time.
static void write_data(address base, address top, address runtime_base) {
static void log_data(address base, address top, address runtime_base, bool is_heap = false) {
assert(top >= base, "must be");

LogStreamHandle(Trace, cds, map) lsh;
if (lsh.is_enabled()) {
os::print_hex_dump(&lsh, base, top, sizeof(address), 32, runtime_base);
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, 32, runtime_base);
}
}

static void write_header(FileMapInfo* mapinfo) {
static void log_header(FileMapInfo* mapinfo) {
LogStreamHandle(Info, cds, map) lsh;
if (lsh.is_enabled()) {
mapinfo->print(&lsh);
}
}

public:
static void write(ArchiveBuilder* builder, FileMapInfo* mapinfo,
GrowableArray<MemRegion> *closed_heap_regions,
GrowableArray<MemRegion> *open_heap_regions,
char* bitmap, size_t bitmap_size_in_bytes) {
static void log(ArchiveBuilder* builder, FileMapInfo* mapinfo,
GrowableArray<MemRegion> *closed_heap_regions,
GrowableArray<MemRegion> *open_heap_regions,
char* bitmap, size_t bitmap_size_in_bytes) {
log_info(cds, map)("%s CDS archive map for %s", DumpSharedSpaces ? "Static" : "Dynamic", mapinfo->full_path());

address header = address(mapinfo->header());
address header_end = header + mapinfo->header()->header_size();
write_region("header", header, header_end, 0);
write_header(mapinfo);
write_data(header, header_end, 0);
log_region("header", header, header_end, 0);
log_header(mapinfo);
log_data(header, header_end, 0);

DumpRegion* rw_region = &builder->_rw_region;
DumpRegion* ro_region = &builder->_ro_region;

write_dump_region("rw region", rw_region);
write_objects(rw_region, &builder->_rw_src_objs);

write_dump_region("ro region", ro_region);
write_objects(ro_region, &builder->_ro_src_objs);
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);
write_region("bitmap", address(bitmap), bitmap_end, 0);
write_data(header, header_end, 0);
log_region("bitmap", address(bitmap), bitmap_end, 0);
log_data((address)bitmap, bitmap_end, 0);

if (closed_heap_regions != NULL) {
write_heap_region("closed heap region", closed_heap_regions);
log_heap_regions("closed heap region", closed_heap_regions);
}
if (open_heap_regions != NULL) {
write_heap_region("open heap region", open_heap_regions);
log_heap_regions("open heap region", open_heap_regions);
}

log_info(cds, 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()));
Expand Down Expand Up @@ -1140,9 +1177,10 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo,
}

if (log_is_enabled(Info, cds, map)) {
CDSMapLogger::write(this, mapinfo, closed_heap_regions, open_heap_regions,
bitmap, bitmap_size_in_bytes);
CDSMapLogger::log(this, mapinfo, closed_heap_regions, open_heap_regions,
bitmap, bitmap_size_in_bytes);
}
CDS_JAVA_HEAP_ONLY(HeapShared::destroy_archived_object_cache());
FREE_C_HEAP_ARRAY(char, bitmap);
}

Expand Down
16 changes: 13 additions & 3 deletions src/hotspot/share/cds/heapShared.cpp
Expand Up @@ -51,8 +51,9 @@
#include "memory/universe.hpp"
#include "oops/compressedOops.inline.hpp"
#include "oops/fieldStreams.inline.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/globals_extension.hpp"
Expand Down Expand Up @@ -213,6 +214,7 @@ void HeapShared::reset_archived_object_states(TRAPS) {
}

HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = NULL;
HeapShared::OriginalObjectTable* HeapShared::_original_object_table = NULL;
oop HeapShared::find_archived_heap_object(oop obj) {
assert(DumpSharedSpaces, "dump-time only");
ArchivedObjectCache* cache = archived_object_cache();
Expand Down Expand Up @@ -317,6 +319,9 @@ oop HeapShared::archive_object(oop obj) {
ArchivedObjectCache* cache = archived_object_cache();
CachedOopInfo info = make_cached_oop_info(archived_oop);
cache->put(obj, info);
if (_original_object_table != NULL) {
_original_object_table->put(archived_oop, obj);
}
if (log_is_enabled(Debug, cds, heap)) {
ResourceMark rm;
log_debug(cds, heap)("Archived heap object " PTR_FORMAT " ==> " PTR_FORMAT " : %s",
Expand Down Expand Up @@ -466,7 +471,7 @@ void HeapShared::archive_objects(GrowableArray<MemRegion>* closed_regions,
NoSafepointVerifier nsv;

// Cache for recording where the archived objects are copied to
create_archived_object_cache();
create_archived_object_cache(log_is_enabled(Info, cds, map));

log_info(cds)("Heap range = [" PTR_FORMAT " - " PTR_FORMAT "]",
UseCompressedOops ? p2i(CompressedOops::begin()) :
Expand All @@ -480,7 +485,6 @@ void HeapShared::archive_objects(GrowableArray<MemRegion>* closed_regions,
copy_open_objects(open_regions);

CDSHeapVerifier::verify();
destroy_archived_object_cache();
}

G1HeapVerifier::verify_archive_regions();
Expand Down Expand Up @@ -532,6 +536,12 @@ void HeapShared::copy_open_objects(GrowableArray<MemRegion>* open_regions) {

// Copy _pending_archive_roots into an objArray
void HeapShared::copy_roots() {
// HeapShared::roots() points into an ObjArray in the open archive region. A portion of the
// objects in this array are discovered during HeapShared::archive_objects(). For example,
// in HeapShared::archive_reachable_objects_from() -> HeapShared::check_enum_obj().
// However, HeapShared::archive_objects() happens inside a safepoint, so we can't
// allocate a "regular" ObjArray and pass the result to HeapShared::archive_object().
// Instead, we have to roll our own alloc/copy routine here.
int length = _pending_roots != NULL ? _pending_roots->length() : 0;
size_t size = objArrayOopDesc::object_size(length);
Klass* k = Universe::objectArrayKlassObj(); // already relocated to point to archived klass
Expand Down
30 changes: 28 additions & 2 deletions src/hotspot/share/cds/heapShared.hpp
Expand Up @@ -247,12 +247,19 @@ class HeapShared: AllStatic {
}

typedef ResourceHashtable<oop, CachedOopInfo,
15889, // prime number
36137, // prime number
ResourceObj::C_HEAP,
mtClassShared,
HeapShared::oop_hash> ArchivedObjectCache;
static ArchivedObjectCache* _archived_object_cache;

typedef ResourceHashtable<oop, oop,
36137, // prime number
ResourceObj::C_HEAP,
mtClassShared,
HeapShared::oop_hash> OriginalObjectTable;
static OriginalObjectTable* _original_object_table;

class DumpTimeKlassSubGraphInfoTable
: public ResourceHashtable<Klass*, KlassSubGraphInfo,
137, // prime number
Expand Down Expand Up @@ -375,17 +382,36 @@ class HeapShared: AllStatic {
static void fill_failed_loaded_region();
public:
static void reset_archived_object_states(TRAPS);
static void create_archived_object_cache() {
static void create_archived_object_cache(bool create_orig_table) {
_archived_object_cache =
new (ResourceObj::C_HEAP, mtClass)ArchivedObjectCache();
if (create_orig_table) {
_original_object_table =
new (ResourceObj::C_HEAP, mtClass)OriginalObjectTable();
} else {
_original_object_table = NULL;
}
}
static void destroy_archived_object_cache() {
delete _archived_object_cache;
_archived_object_cache = NULL;
if (_original_object_table != NULL) {
delete _original_object_table;
_original_object_table = NULL;
}
}
static ArchivedObjectCache* archived_object_cache() {
return _archived_object_cache;
}
static oop get_original_object(oop archived_object) {
assert(_original_object_table != NULL, "sanity");
oop* r = _original_object_table->get(archived_object);
if (r == NULL) {
return NULL;
} else {
return *r;
}
}

static oop find_archived_heap_object(oop obj);
static oop archive_object(oop obj);
Expand Down

1 comment on commit c1e67b6

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.