8243506: SharedBaseAddress is ignored by -Xshare:dump

Reviewed-by: stuefe, ccheung
This commit is contained in:
Ioi Lam 2020-06-02 01:08:44 -07:00
parent b5775c831d
commit f39a71cafe
10 changed files with 199 additions and 49 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2020, 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
@ -31,6 +31,7 @@
#include "memory/heapShared.inline.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/metaspaceShared.hpp"
#include "runtime/globals.hpp"
#include "runtime/vmThread.hpp"
#include "utilities/numberSeq.hpp"
#include <sys/stat.h>
@ -212,11 +213,13 @@ size_t SimpleCompactHashtable::calculate_header_size() {
void SimpleCompactHashtable::serialize_header(SerializeClosure* soc) {
// NOTE: if you change this function, you MUST change the number 5 in
// calculate_header_size() accordingly.
soc->do_ptr((void**)&_base_address);
soc->do_u4(&_entry_count);
soc->do_u4(&_bucket_count);
soc->do_ptr((void**)&_buckets);
soc->do_ptr((void**)&_entries);
if (soc->reading()) {
_base_address = (address)SharedBaseAddress;
}
}
#endif // INCLUDE_CDS

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2020, 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
@ -58,6 +58,12 @@ void ArchivePtrMarker::mark_pointer(address* ptr_loc) {
if (_ptr_base <= ptr_loc && ptr_loc < _ptr_end) {
address value = *ptr_loc;
// We don't want any pointer that points to very bottom of the archive, otherwise when
// MetaspaceShared::default_base_address()==0, we can't distinguish between a pointer
// to nothing (NULL) vs a pointer to an objects that happens to be at the very bottom
// of the archive.
assert(value != (address)_ptr_base, "don't point to the bottom of the archive");
if (value != NULL) {
assert(uintx(ptr_loc) % sizeof(intptr_t) == 0, "pointers must be stored in aligned addresses");
size_t idx = ptr_loc - _ptr_base;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2020, 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
@ -52,6 +52,7 @@ inline bool SharedDataRelocator<COMPACTING>::do_bit(size_t offset) {
}
address new_ptr = old_ptr + _delta;
assert(new_ptr != NULL, "don't point to the bottom of the archive"); // See ArchivePtrMarker::mark_pointer().
assert(_valid_new_base <= new_ptr && new_ptr < _valid_new_end, "must be");
DEBUG_ONLY(log_trace(cds, reloc)("Patch2: @%8d [" PTR_FORMAT "] " PTR_FORMAT " -> " PTR_FORMAT,

View file

@ -966,13 +966,13 @@ void DynamicArchiveBuilder::relocate_buffer_to_target() {
if (addr_delta == 0) {
ArchivePtrMarker::compact(relocatable_base, relocatable_end);
} else {
// The base archive is NOT mapped at Arguments::default_SharedBaseAddress() (due to ASLR).
// The base archive is NOT mapped at MetaspaceShared::requested_base_address() (due to ASLR).
// This means that the current content of the dynamic archive is based on a random
// address. Let's relocate all the pointers, so that it can be mapped to
// Arguments::default_SharedBaseAddress() without runtime relocation.
// MetaspaceShared::requested_base_address() without runtime relocation.
//
// Note: both the base and dynamic archive are written with
// FileMapHeader::_shared_base_address == Arguments::default_SharedBaseAddress()
// FileMapHeader::_requested_base_address == MetaspaceShared::requested_base_address()
// Patch all pointers that are marked by ptrmap within this region,
// where we have just dumped all the metaspace data.
@ -992,7 +992,7 @@ void DynamicArchiveBuilder::relocate_buffer_to_target() {
// after patching, the pointers must point inside this range
// (the requested location of the archive, as mapped at runtime).
address valid_new_base = (address)Arguments::default_SharedBaseAddress();
address valid_new_base = (address)MetaspaceShared::requested_base_address();
address valid_new_end = valid_new_base + base_plus_top_size;
log_debug(cds)("Relocating archive from [" INTPTR_FORMAT " - " INTPTR_FORMAT "] to "
@ -1020,7 +1020,7 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data) {
const char* archive_name = Arguments::GetSharedDynamicArchivePath();
dynamic_info->open_for_write(archive_name);
MetaspaceShared::write_core_archive_regions(dynamic_info, NULL, NULL);
dynamic_info->set_final_requested_base((char*)Arguments::default_SharedBaseAddress());
dynamic_info->set_final_requested_base((char*)MetaspaceShared::requested_base_address());
dynamic_info->set_header_crc(dynamic_info->compute_header_crc());
dynamic_info->write_header();
dynamic_info->close();

View file

@ -88,6 +88,7 @@ address MetaspaceShared::_i2i_entry_code_buffers = NULL;
size_t MetaspaceShared::_i2i_entry_code_buffers_size = 0;
void* MetaspaceShared::_shared_metaspace_static_top = NULL;
intx MetaspaceShared::_relocation_delta;
char* MetaspaceShared::_requested_base_address;
// The CDS archive is divided into the following regions:
// mc - misc code (the method entry trampolines, c++ vtables)
@ -240,33 +241,53 @@ char* MetaspaceShared::read_only_space_alloc(size_t num_bytes) {
size_t MetaspaceShared::reserved_space_alignment() { return os::vm_allocation_granularity(); }
static bool shared_base_valid(char* shared_base) {
#ifdef _LP64
// Check SharedBaseAddress for validity. At this point, os::init() must
// have been ran.
static void check_SharedBaseAddress() {
SharedBaseAddress = align_up(SharedBaseAddress,
MetaspaceShared::reserved_space_alignment());
if (!CompressedKlassPointers::is_valid_base((address)SharedBaseAddress)) {
log_warning(cds)("SharedBaseAddress=" PTR_FORMAT " is invalid for this "
"platform, option will be ignored.",
p2i((address)SharedBaseAddress));
SharedBaseAddress = Arguments::default_SharedBaseAddress();
}
}
return CompressedKlassPointers::is_valid_base((address)shared_base);
#else
return true;
#endif
}
static bool shared_base_too_high(char* shared_base, size_t cds_total) {
if (SharedBaseAddress != 0 && shared_base < (char*)SharedBaseAddress) {
// SharedBaseAddress is very high (e.g., 0xffffffffffffff00) so
// align_up(SharedBaseAddress, MetaspaceShared::reserved_space_alignment()) has wrapped around.
return true;
}
if (max_uintx - uintx(shared_base) < uintx(cds_total)) {
// The end of the archive will wrap around
return true;
}
return false;
}
static char* compute_shared_base(size_t cds_total) {
char* shared_base = (char*)align_up((char*)SharedBaseAddress, MetaspaceShared::reserved_space_alignment());
const char* err = NULL;
if (shared_base_too_high(shared_base, cds_total)) {
err = "too high";
} else if (!shared_base_valid(shared_base)) {
err = "invalid for this platform";
}
if (err) {
log_warning(cds)("SharedBaseAddress (" INTPTR_FORMAT ") is %s. Reverted to " INTPTR_FORMAT,
p2i((void*)SharedBaseAddress), err,
p2i((void*)Arguments::default_SharedBaseAddress()));
SharedBaseAddress = Arguments::default_SharedBaseAddress();
shared_base = (char*)align_up((char*)SharedBaseAddress, MetaspaceShared::reserved_space_alignment());
}
assert(!shared_base_too_high(shared_base, cds_total) && shared_base_valid(shared_base), "Sanity");
return shared_base;
}
void MetaspaceShared::initialize_dumptime_shared_and_meta_spaces() {
assert(DumpSharedSpaces, "should be called for dump time only");
#ifdef _LP64
check_SharedBaseAddress();
#endif
const size_t reserve_alignment = MetaspaceShared::reserved_space_alignment();
char* shared_base = (char*)align_up((char*)SharedBaseAddress, reserve_alignment);
#ifdef _LP64
assert(CompressedKlassPointers::is_valid_base((address)shared_base), "Sanity");
// On 64-bit VM we reserve a 4G range and, if UseCompressedClassPointers=1,
// will use that to house both the archives and the ccs. See below for
// details.
@ -278,6 +299,9 @@ void MetaspaceShared::initialize_dumptime_shared_and_meta_spaces() {
size_t cds_total = align_down(256*M, reserve_alignment);
#endif
char* shared_base = compute_shared_base(cds_total);
_requested_base_address = shared_base;
// Whether to use SharedBaseAddress as attach address.
bool use_requested_base = true;
@ -398,6 +422,10 @@ void MetaspaceShared::initialize_dumptime_shared_and_meta_spaces() {
log_info(cds)("Allocated shared space: " SIZE_FORMAT " bytes at " PTR_FORMAT,
_shared_rs.size(), p2i(_shared_rs.base()));
// We don't want any valid object to be at the very bottom of the archive.
// See ArchivePtrMarker::mark_pointer().
MetaspaceShared::misc_code_space_alloc(16);
size_t symbol_rs_size = LP64_ONLY(3 * G) NOT_LP64(128 * M);
_symbol_rs = ReservedSpace(symbol_rs_size);
if (!_symbol_rs.is_reserved()) {
@ -1200,7 +1228,7 @@ private:
void print_bitmap_region_stats(size_t size, size_t total_size);
void print_heap_region_stats(GrowableArray<MemRegion> *heap_mem,
const char *name, size_t total_size);
void relocate_to_default_base_address(CHeapBitMap* ptrmap);
void relocate_to_requested_base_address(CHeapBitMap* ptrmap);
public:
@ -1568,18 +1596,18 @@ void VM_PopulateDumpSharedSpace::print_class_stats() {
}
}
void VM_PopulateDumpSharedSpace::relocate_to_default_base_address(CHeapBitMap* ptrmap) {
void VM_PopulateDumpSharedSpace::relocate_to_requested_base_address(CHeapBitMap* ptrmap) {
intx addr_delta = MetaspaceShared::final_delta();
if (addr_delta == 0) {
ArchivePtrMarker::compact((address)SharedBaseAddress, (address)_ro_region.top());
} else {
// We are not able to reserve space at Arguments::default_SharedBaseAddress() (due to ASLR).
// We are not able to reserve space at MetaspaceShared::requested_base_address() (due to ASLR).
// This means that the current content of the archive is based on a random
// address. Let's relocate all the pointers, so that it can be mapped to
// Arguments::default_SharedBaseAddress() without runtime relocation.
// MetaspaceShared::requested_base_address() without runtime relocation.
//
// Note: both the base and dynamic archive are written with
// FileMapHeader::_shared_base_address == Arguments::default_SharedBaseAddress()
// FileMapHeader::_requested_base_address == MetaspaceShared::requested_base_address()
// Patch all pointers that are marked by ptrmap within this region,
// where we have just dumped all the metaspace data.
@ -1594,7 +1622,7 @@ void VM_PopulateDumpSharedSpace::relocate_to_default_base_address(CHeapBitMap* p
// after patching, the pointers must point inside this range
// (the requested location of the archive, as mapped at runtime).
address valid_new_base = (address)Arguments::default_SharedBaseAddress();
address valid_new_base = (address)MetaspaceShared::requested_base_address();
address valid_new_end = valid_new_base + size;
log_debug(cds)("Relocating archive from [" INTPTR_FORMAT " - " INTPTR_FORMAT " ] to "
@ -1681,9 +1709,9 @@ void VM_PopulateDumpSharedSpace::doit() {
memset(MetaspaceShared::i2i_entry_code_buffers(), 0,
MetaspaceShared::i2i_entry_code_buffers_size());
// relocate the data so that it can be mapped to Arguments::default_SharedBaseAddress()
// relocate the data so that it can be mapped to MetaspaceShared::requested_base_address()
// without runtime relocation.
relocate_to_default_base_address(&ptrmap);
relocate_to_requested_base_address(&ptrmap);
// Create and write the archive file that maps the shared spaces.
@ -1706,7 +1734,7 @@ void VM_PopulateDumpSharedSpace::doit() {
MetaspaceShared::first_open_archive_heap_region,
MetaspaceShared::max_open_archive_heap_region);
mapinfo->set_final_requested_base((char*)Arguments::default_SharedBaseAddress());
mapinfo->set_final_requested_base((char*)MetaspaceShared::requested_base_address());
mapinfo->set_header_crc(mapinfo->compute_header_crc());
mapinfo->write_header();
print_region_stats(mapinfo);
@ -2162,6 +2190,7 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() {
} else {
FileMapInfo::set_shared_path_table(static_mapinfo);
}
_requested_base_address = static_mapinfo->requested_base_address();
} else {
set_shared_metaspace_range(NULL, NULL, NULL);
UseSharedSpaces = false;
@ -2209,6 +2238,11 @@ FileMapInfo* MetaspaceShared::open_dynamic_archive() {
// false = map at an alternative address picked by OS.
MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo,
bool use_requested_addr) {
if (use_requested_addr && static_mapinfo->requested_base_address() == NULL) {
log_info(cds)("Archive(s) were created with -XX:SharedBaseAddress=0. Always map at os-selected address.");
return MAP_ARCHIVE_MMAP_FAILURE;
}
PRODUCT_ONLY(if (ArchiveRelocationMode == 1 && use_requested_addr) {
// For product build only -- this is for benchmarking the cost of doing relocation.
// For debug builds, the check is done below, after reserving the space, for better test coverage
@ -2660,11 +2694,11 @@ void MetaspaceShared::report_out_of_space(const char* name, size_t needed_bytes)
"Please reduce the number of shared classes.");
}
// This is used to relocate the pointers so that the archive can be mapped at
// Arguments::default_SharedBaseAddress() without runtime relocation.
// This is used to relocate the pointers so that the base archive can be mapped at
// MetaspaceShared::requested_base_address() without runtime relocation.
intx MetaspaceShared::final_delta() {
return intx(Arguments::default_SharedBaseAddress()) // We want the archive to be mapped to here at runtime
- intx(SharedBaseAddress); // .. but the archive is mapped at here at dump time
return intx(MetaspaceShared::requested_base_address()) // We want the base archive to be mapped to here at runtime
- intx(SharedBaseAddress); // .. but the base archive is mapped at here at dump time
}
void MetaspaceShared::print_on(outputStream* st) {

View file

@ -183,6 +183,7 @@ class MetaspaceShared : AllStatic {
static size_t _core_spaces_size;
static void* _shared_metaspace_static_top;
static intx _relocation_delta;
static char* _requested_base_address;
public:
enum {
// core archive spaces
@ -353,6 +354,12 @@ class MetaspaceShared : AllStatic {
static intptr_t* fix_cpp_vtable_for_dynamic_archive(MetaspaceObj::Type msotype, address obj);
static void initialize_ptr_marker(CHeapBitMap* ptrmap);
// This is the base address as specified by -XX:SharedBaseAddress during -Xshare:dump.
// Both the base/top archives are written using this as their base address.
static char* requested_base_address() {
return _requested_base_address;
}
// Non-zero if the archive(s) need to be mapped a non-default location due to ASLR.
static intx relocation_delta() { return _relocation_delta; }
static intx final_delta();

View file

@ -85,7 +85,7 @@ bool Arguments::_BackgroundCompilation = BackgroundCompilation;
bool Arguments::_ClipInlining = ClipInlining;
intx Arguments::_Tier3InvokeNotifyFreqLog = Tier3InvokeNotifyFreqLog;
intx Arguments::_Tier4InvocationThreshold = Tier4InvocationThreshold;
size_t Arguments::_SharedBaseAddress = SharedBaseAddress;
size_t Arguments::_default_SharedBaseAddress = SharedBaseAddress;
bool Arguments::_enable_preview = false;
@ -2281,8 +2281,8 @@ jint Arguments::parse_vm_init_args(const JavaVMInitArgs *vm_options_args,
Arguments::_Tier4InvocationThreshold = Tier4InvocationThreshold;
}
// CDS dumping always write the archive to the default value of SharedBaseAddress.
Arguments::_SharedBaseAddress = SharedBaseAddress;
// Remember the default value of SharedBaseAddress.
Arguments::_default_SharedBaseAddress = SharedBaseAddress;
// Setup flags for mixed which is the default
set_mode_flags(_mixed);

View file

@ -489,7 +489,7 @@ class Arguments : AllStatic {
static char* SharedArchivePath;
static char* SharedDynamicArchivePath;
static size_t _SharedBaseAddress; // The default value specified in globals.hpp
static size_t _default_SharedBaseAddress; // The default value specified in globals.hpp
static int num_archives(const char* archive_path) NOT_CDS_RETURN_(0);
static void extract_shared_archive_paths(const char* archive_path,
char** base_archive_path,
@ -572,7 +572,7 @@ class Arguments : AllStatic {
static const char* GetSharedArchivePath() { return SharedArchivePath; }
static const char* GetSharedDynamicArchivePath() { return SharedDynamicArchivePath; }
static size_t default_SharedBaseAddress() { return _SharedBaseAddress; }
static size_t default_SharedBaseAddress() { return _default_SharedBaseAddress; }
// Java launcher properties
static void process_sun_java_launcher_properties(JavaVMInitArgs* args);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, 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
@ -40,7 +40,13 @@ public class SharedBaseAddress {
private static final String[] testTable = {
"1g", "8g", "64g","512g", "4t",
"32t", "128t", "0",
"1", "64k", "64M"
"1", "64k", "64M",
"0xfffffffffff00000", // archive top wraps around 64-bit address space
"0xfff80000", // archive top wraps around 32-bit address space
"0xffffffffffffffff", // archive bottom wraps around 64-bit address space -- due to align_up()
"0xffffffff", // archive bottom wraps around 32-bit address space -- due to align_up()
"0x00007ffffff00000", // end of archive will go past the end of user space on linux/x64
"0", // always let OS pick the base address at runtime (ASLR for CDS archive)
};
public static void main(String[] args) throws Exception {
@ -50,10 +56,16 @@ public class SharedBaseAddress {
System.out.println("sharedBaseAddress = " + testEntry);
CDSOptions opts = (new CDSOptions())
.setArchiveName(filename)
.addPrefix("-XX:SharedBaseAddress=" + testEntry);
.addPrefix("-XX:SharedBaseAddress=" + testEntry)
.addPrefix("-Xlog:cds=debug")
.addPrefix("-Xlog:cds+reloc=debug");
CDSTestUtils.createArchiveAndCheck(opts);
CDSTestUtils.runWithArchiveAndCheck(opts);
OutputAnalyzer out = CDSTestUtils.runWithArchiveAndCheck(opts);
if (testEntry.equals("0")) {
out.shouldContain("Archive(s) were created with -XX:SharedBaseAddress=0. Always map at os-selected address.")
.shouldContain("Try to map archive(s) at an alternative address");
}
}
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2020, 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
* @summary Test how the dynamic archive handles -XX:SharedBaseAddress
* @requires vm.cds
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* @build Hello
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller -jar hello.jar Hello
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. SharedBaseAddressOption
*/
import jdk.test.lib.Platform;
public class SharedBaseAddressOption extends DynamicArchiveTestBase {
static String appJar = ClassFileInstaller.getJarPath("hello.jar");
static String mainClass = "Hello";
public static void main(String[] args) throws Exception {
runTest(SharedBaseAddressOption::testCustomBase);
}
static void testCustomBase() throws Exception {
// (1) -XX:SharedBaseAddress=0 -- the archives will always be relocated at runtime
doTest("0");
// (2) -XX:SharedBaseAddress=0x810000000
if (Platform.is64bit()) {
doTest("0x810000000");
}
// (3) -XX:SharedBaseAddress that's so high that the archive may wrap around 64-bit
if (Platform.is64bit()) {
doTest("0xfffffffffff00000");
}
}
static void doTest(String sharedBase) throws Exception {
String baseArchiveName = getNewArchiveName("base");
String topArchiveName = getNewArchiveName("top");
TestCommon.dumpBaseArchive(baseArchiveName, "-XX:SharedBaseAddress=" + sharedBase,
"-Xlog:cds=debug",
"-Xlog:cds+reloc=debug",
"-Xlog:cds+dynamic=debug");
dump2(baseArchiveName, topArchiveName,
"-Xlog:cds=debug",
"-Xlog:cds+reloc=debug",
"-Xlog:cds+dynamic=debug",
"-cp", appJar, mainClass)
.assertNormalExit();
// a top archive specified in the base archive position
run2(baseArchiveName, topArchiveName,
"-Xlog:cds=debug",
"-Xlog:cds+reloc=debug",
"-Xlog:cds+dynamic=debug",
"-cp", appJar, mainClass)
.assertNormalExit();
}
}