8078644: CDS needs to support JVMTI CFLH

Support posting CLFH for shared classes. Tests are contributed by Misha Seledtsov.

Reviewed-by: iklam, coleenp, acorn, dcubed, sspitsyn
This commit is contained in:
Jiangli Zhou 2016-09-18 21:10:48 -04:00
parent 38eb4a4f6f
commit eb52950704
28 changed files with 1256 additions and 68 deletions

@ -25,12 +25,85 @@
#include "precompiled.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.hpp"
#include "classfile/classLoaderData.inline.hpp"
#include "classfile/klassFactory.hpp"
#include "classfile/sharedClassUtil.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "prims/jvmtiEnvBase.hpp"
#include "prims/jvmtiRedefineClasses.hpp"
#include "trace/traceMacros.hpp"
// called during initial loading of a shared class
instanceKlassHandle KlassFactory::check_shared_class_file_load_hook(
instanceKlassHandle ik,
Symbol* class_name,
Handle class_loader,
Handle protection_domain, TRAPS) {
#if INCLUDE_CDS && INCLUDE_JVMTI
assert(ik.not_null(), "sanity");
assert(ik()->is_shared(), "expecting a shared class");
if (JvmtiExport::should_post_class_file_load_hook()) {
assert(THREAD->is_Java_thread(), "must be JavaThread");
// Post the CFLH
JvmtiCachedClassFileData* cached_class_file = NULL;
JvmtiCachedClassFileData* archived_class_data = ik->get_archived_class_data();
assert(archived_class_data != NULL, "shared class has no archived class data");
unsigned char* ptr =
VM_RedefineClasses::get_cached_class_file_bytes(archived_class_data);
unsigned char* end_ptr =
ptr + VM_RedefineClasses::get_cached_class_file_len(archived_class_data);
unsigned char* old_ptr = ptr;
JvmtiExport::post_class_file_load_hook(class_name,
class_loader,
protection_domain,
&ptr,
&end_ptr,
&cached_class_file);
if (old_ptr != ptr) {
// JVMTI agent has modified class file data.
// Set new class file stream using JVMTI agent modified class file data.
ClassLoaderData* loader_data =
ClassLoaderData::class_loader_data(class_loader());
int path_index = ik->shared_classpath_index();
SharedClassPathEntry* ent =
(SharedClassPathEntry*)FileMapInfo::shared_classpath(path_index);
ClassFileStream* stream = new ClassFileStream(ptr,
end_ptr - ptr,
ent->_name,
ClassFileStream::verify);
ClassFileParser parser(stream,
class_name,
loader_data,
protection_domain,
NULL,
NULL,
ClassFileParser::BROADCAST, // publicity level
CHECK_NULL);
instanceKlassHandle new_ik = parser.create_instance_klass(true /* changed_by_loadhook */,
CHECK_NULL);
if (cached_class_file != NULL) {
new_ik->set_cached_class_file(cached_class_file);
}
if (class_loader.is_null()) {
ResourceMark rm;
ClassLoader::add_package(class_name->as_C_string(), path_index, THREAD);
}
return new_ik;
}
}
#endif
return NULL;
}
static ClassFileStream* check_class_file_load_hook(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
@ -97,7 +170,6 @@ instanceKlassHandle KlassFactory::create_from_stream(ClassFileStream* stream,
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
TRAPS) {
assert(stream != NULL, "invariant");
assert(loader_data != NULL, "invariant");
assert(THREAD->is_Java_thread(), "must be a JavaThread");
@ -142,5 +214,27 @@ instanceKlassHandle KlassFactory::create_from_stream(ClassFileStream* stream,
TRACE_KLASS_CREATION(result, parser, THREAD);
#if INCLUDE_CDS && INCLUDE_JVMTI
if (DumpSharedSpaces) {
assert(cached_class_file == NULL, "Sanity");
// Archive the class stream data into the optional data section
JvmtiCachedClassFileData *p;
int len;
const unsigned char *bytes;
// event based tracing might set cached_class_file
if ((bytes = result->get_cached_class_file_bytes()) != NULL) {
len = result->get_cached_class_file_len();
} else {
len = stream->length();
bytes = stream->buffer();
}
p = (JvmtiCachedClassFileData*)MetaspaceShared::optional_data_space_alloc(
offset_of(JvmtiCachedClassFileData, data) + len);
p->length = len;
memcpy(p->data, bytes, len);
result->set_archived_class_data(p);
}
#endif
return result;
}

@ -75,6 +75,12 @@ class KlassFactory : AllStatic {
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
TRAPS);
public:
static instanceKlassHandle check_shared_class_file_load_hook(
instanceKlassHandle ik,
Symbol* class_name,
Handle class_loader,
Handle protection_domain, TRAPS);
};
#endif // SHARE_VM_CLASSFILE_KLASSFACTORY_HPP

@ -1210,16 +1210,12 @@ Klass* SystemDictionary::find_shared_class(Symbol* class_name) {
instanceKlassHandle SystemDictionary::load_shared_class(
Symbol* class_name, Handle class_loader, TRAPS) {
// Don't load shared class when JvmtiExport::should_post_class_file_load_hook()
// is enabled since posting CFLH is not supported when loading shared class.
if (!JvmtiExport::should_post_class_file_load_hook()) {
instanceKlassHandle ik (THREAD, find_shared_class(class_name));
// Make sure we only return the boot class for the NULL classloader.
if (ik.not_null() &&
ik->is_shared_boot_class() && class_loader.is_null()) {
Handle protection_domain;
return load_shared_class(ik, class_loader, protection_domain, THREAD);
}
instanceKlassHandle ik (THREAD, find_shared_class(class_name));
// Make sure we only return the boot class for the NULL classloader.
if (ik.not_null() &&
ik->is_shared_boot_class() && class_loader.is_null()) {
Handle protection_domain;
return load_shared_class(ik, class_loader, protection_domain, THREAD);
}
return instanceKlassHandle();
}
@ -1303,11 +1299,6 @@ instanceKlassHandle SystemDictionary::load_shared_class(instanceKlassHandle ik,
Handle class_loader,
Handle protection_domain, TRAPS) {
instanceKlassHandle nh = instanceKlassHandle(); // null Handle
if (JvmtiExport::should_post_class_file_load_hook()) {
// Don't load shared class when JvmtiExport::should_post_class_file_load_hook()
// is enabled since posting CFLH is not supported when loading shared class.
return nh;
}
if (ik.not_null()) {
Symbol* class_name = ik->name();
@ -1358,6 +1349,14 @@ instanceKlassHandle SystemDictionary::load_shared_class(instanceKlassHandle ik,
}
}
instanceKlassHandle new_ik = KlassFactory::check_shared_class_file_load_hook(
ik, class_name, class_loader, protection_domain, CHECK_(nh));
if (new_ik.not_null()) {
// The class is changed by CFLH. Return the new class. The shared class is
// not used.
return new_ik;
}
// Adjust methods to recover missing data. They need addresses for
// interpreter entry points and their default native method address
// must be reset.

@ -649,7 +649,7 @@ ReservedSpace FileMapInfo::reserve_shared_memory() {
// Memory map a region in the address space.
static const char* shared_region_name[] = { "ReadOnly", "ReadWrite", "MiscData", "MiscCode",
"String1", "String2" };
"String1", "String2", "OptionalData" };
char* FileMapInfo::map_region(int i) {
assert(!MetaspaceShared::is_string_region(i), "sanity");

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2016, 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
@ -252,10 +252,27 @@ public:
bool is_in_shared_region(const void* p, int idx) NOT_CDS_RETURN_(false);
void print_shared_spaces() NOT_CDS_RETURN;
// The ro+rw+md+mc spaces size
static size_t core_spaces_size() {
return align_size_up((SharedReadOnlySize + SharedReadWriteSize +
SharedMiscDataSize + SharedMiscCodeSize),
os::vm_allocation_granularity());
}
// The estimated optional space size.
//
// Currently the optional space only has archived class bytes.
// The core_spaces_size is the size of all class metadata, which is a good
// estimate of the total class bytes to be archived. Only the portion
// containing data is written out to the archive and mapped at runtime.
// There is no memory waste due to unused portion in optional space.
static size_t optional_space_size() {
return core_spaces_size();
}
// Total shared_spaces size includes the ro, rw, md, mc and od spaces
static size_t shared_spaces_size() {
return align_size_up(SharedReadOnlySize + SharedReadWriteSize +
SharedMiscDataSize + SharedMiscCodeSize,
os::vm_allocation_granularity());
return core_spaces_size() + optional_space_size();
}
// Stop CDS sharing and unmap CDS regions.

@ -3172,36 +3172,28 @@ void Metaspace::global_initialize() {
address cds_address = NULL;
FileMapInfo* mapinfo = new FileMapInfo();
if (JvmtiExport::should_post_class_file_load_hook()) {
// Currently CDS does not support JVMTI CFLH when loading shared class.
// If JvmtiExport::should_post_class_file_load_hook is already enabled,
// just disable UseSharedSpaces.
FileMapInfo::fail_continue("Tool agent requires sharing to be disabled.");
delete mapinfo;
} else {
// Open the shared archive file, read and validate the header. If
// initialization fails, shared spaces [UseSharedSpaces] are
// disabled and the file is closed.
// Map in spaces now also
if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) {
cds_total = FileMapInfo::shared_spaces_size();
cds_address = (address)mapinfo->header()->region_addr(0);
// Open the shared archive file, read and validate the header. If
// initialization fails, shared spaces [UseSharedSpaces] are
// disabled and the file is closed.
// Map in spaces now also
if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) {
cds_total = FileMapInfo::shared_spaces_size();
cds_address = (address)mapinfo->header()->region_addr(0);
#ifdef _LP64
if (using_class_space()) {
char* cds_end = (char*)(cds_address + cds_total);
cds_end = (char *)align_ptr_up(cds_end, _reserve_alignment);
// If UseCompressedClassPointers is set then allocate the metaspace area
// above the heap and above the CDS area (if it exists).
allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address);
// Map the shared string space after compressed pointers
// because it relies on compressed class pointers setting to work
mapinfo->map_string_regions();
}
#endif // _LP64
} else {
assert(!mapinfo->is_open() && !UseSharedSpaces,
"archive file not closed or shared spaces not disabled.");
if (using_class_space()) {
char* cds_end = (char*)(cds_address + cds_total);
cds_end = (char *)align_ptr_up(cds_end, _reserve_alignment);
// If UseCompressedClassPointers is set then allocate the metaspace area
// above the heap and above the CDS area (if it exists).
allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address);
// Map the shared string space after compressed pointers
// because it relies on compressed class pointers setting to work
mapinfo->map_string_regions();
}
#endif // _LP64
} else {
assert(!mapinfo->is_open() && !UseSharedSpaces,
"archive file not closed or shared spaces not disabled.");
}
}
#endif // INCLUDE_CDS

@ -65,6 +65,7 @@ address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL;
size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0;
SharedMiscRegion MetaspaceShared::_mc;
SharedMiscRegion MetaspaceShared::_md;
SharedMiscRegion MetaspaceShared::_od;
void SharedMiscRegion::initialize(ReservedSpace rs, size_t committed_byte_size, SharedSpaceType space_type) {
_vs.initialize(rs, committed_byte_size);
@ -93,16 +94,24 @@ void MetaspaceShared::initialize_shared_rs(ReservedSpace* rs) {
assert(DumpSharedSpaces, "dump time only");
_shared_rs = rs;
// Split up and initialize the misc code and data spaces
size_t core_spaces_size = FileMapInfo::core_spaces_size();
size_t metadata_size = SharedReadOnlySize + SharedReadWriteSize;
ReservedSpace shared_ro_rw = _shared_rs->first_part(metadata_size);
ReservedSpace misc_section = _shared_rs->last_part(metadata_size);
// Now split into misc sections.
// Split into the core and optional sections
ReservedSpace core_data = _shared_rs->first_part(core_spaces_size);
ReservedSpace optional_data = _shared_rs->last_part(core_spaces_size);
// The RO/RW and the misc sections
ReservedSpace shared_ro_rw = core_data.first_part(metadata_size);
ReservedSpace misc_section = core_data.last_part(metadata_size);
// Now split the misc code and misc data sections.
ReservedSpace md_rs = misc_section.first_part(SharedMiscDataSize);
ReservedSpace mc_rs = misc_section.last_part(SharedMiscDataSize);
_md.initialize(md_rs, SharedMiscDataSize, SharedMiscData);
_mc.initialize(mc_rs, SharedMiscCodeSize, SharedMiscData);
_mc.initialize(mc_rs, SharedMiscCodeSize, SharedMiscCode);
_od.initialize(optional_data, metadata_size, SharedOptional);
}
// Read/write a data stream for restoring/preserving metadata pointers and
@ -521,6 +530,7 @@ private:
GrowableArray<Klass*> *_class_promote_order;
VirtualSpace _md_vs;
VirtualSpace _mc_vs;
VirtualSpace _od_vs;
GrowableArray<MemRegion> *_string_regions;
public:
@ -598,15 +608,19 @@ void VM_PopulateDumpSharedSpace::doit() {
remove_unshareable_in_classes();
tty->print_cr("done. ");
// Set up the share data and shared code segments.
// Set up the misc data, misc code and optional data segments.
_md_vs = *MetaspaceShared::misc_data_region()->virtual_space();
_mc_vs = *MetaspaceShared::misc_code_region()->virtual_space();
_od_vs = *MetaspaceShared::optional_data_region()->virtual_space();
char* md_low = _md_vs.low();
char* md_top = MetaspaceShared::misc_data_region()->alloc_top();
char* md_end = _md_vs.high();
char* mc_low = _mc_vs.low();
char* mc_top = MetaspaceShared::misc_code_region()->alloc_top();
char* mc_end = _mc_vs.high();
char* od_low = _od_vs.low();
char* od_top = MetaspaceShared::optional_data_region()->alloc_top();
char* od_end = _od_vs.high();
// Reserve space for the list of Klass*s whose vtables are used
// for patching others as needed.
@ -661,28 +675,32 @@ void VM_PopulateDumpSharedSpace::doit() {
const size_t rw_alloced = rw_space->capacity_bytes_slow(Metaspace::NonClassType);
const size_t md_alloced = md_end-md_low;
const size_t mc_alloced = mc_end-mc_low;
const size_t od_alloced = od_end-od_low;
const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced
+ ss_bytes;
+ ss_bytes + od_alloced;
// Occupied size of each space.
const size_t ro_bytes = ro_space->used_bytes_slow(Metaspace::NonClassType);
const size_t rw_bytes = rw_space->used_bytes_slow(Metaspace::NonClassType);
const size_t md_bytes = size_t(md_top - md_low);
const size_t mc_bytes = size_t(mc_top - mc_low);
const size_t od_bytes = size_t(od_top - od_low);
// Percent of total size
const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes + ss_bytes;
const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes + ss_bytes + od_bytes;
const double ro_t_perc = ro_bytes / double(total_bytes) * 100.0;
const double rw_t_perc = rw_bytes / double(total_bytes) * 100.0;
const double md_t_perc = md_bytes / double(total_bytes) * 100.0;
const double mc_t_perc = mc_bytes / double(total_bytes) * 100.0;
const double ss_t_perc = ss_bytes / double(total_bytes) * 100.0;
const double od_t_perc = od_bytes / double(total_bytes) * 100.0;
// Percent of fullness of each space
const double ro_u_perc = ro_bytes / double(ro_alloced) * 100.0;
const double rw_u_perc = rw_bytes / double(rw_alloced) * 100.0;
const double md_u_perc = md_bytes / double(md_alloced) * 100.0;
const double mc_u_perc = mc_bytes / double(mc_alloced) * 100.0;
const double od_u_perc = od_bytes / double(od_alloced) * 100.0;
const double total_u_perc = total_bytes / double(total_alloced) * 100.0;
#define fmt_space "%s space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used] at " INTPTR_FORMAT
@ -691,6 +709,7 @@ void VM_PopulateDumpSharedSpace::doit() {
tty->print_cr(fmt_space, "md", md_bytes, md_t_perc, md_alloced, md_u_perc, p2i(md_low));
tty->print_cr(fmt_space, "mc", mc_bytes, mc_t_perc, mc_alloced, mc_u_perc, p2i(mc_low));
tty->print_cr(fmt_space, "st", ss_bytes, ss_t_perc, ss_bytes, 100.0, p2i(ss_low));
tty->print_cr(fmt_space, "od", od_bytes, od_t_perc, od_alloced, od_u_perc, p2i(od_low));
tty->print_cr("total : " SIZE_FORMAT_W(9) " [100.0%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used]",
total_bytes, total_alloced, total_u_perc);
@ -734,6 +753,10 @@ void VM_PopulateDumpSharedSpace::doit() {
SharedMiscCodeSize,
true, true);
mapinfo->write_string_regions(_string_regions);
mapinfo->write_region(MetaspaceShared::od, _od_vs.low(),
pointer_delta(od_top, _od_vs.low(), sizeof(char)),
pointer_delta(od_end, _od_vs.low(), sizeof(char)),
true, false);
}
mapinfo->close();
@ -1049,8 +1072,6 @@ void MetaspaceShared::print_shared_spaces() {
// Map shared spaces at requested addresses and return if succeeded.
// Need to keep the bounds of the ro and rw space for the Metaspace::contains
// call, or is_in_shared_space.
bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
size_t image_alignment = mapinfo->alignment();
@ -1068,6 +1089,7 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
char* _rw_base = NULL;
char* _md_base = NULL;
char* _mc_base = NULL;
char* _od_base = NULL;
// Map each shared region
if ((_ro_base = mapinfo->map_region(ro)) != NULL &&
@ -1078,6 +1100,8 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
mapinfo->verify_region_checksum(md) &&
(_mc_base = mapinfo->map_region(mc)) != NULL &&
mapinfo->verify_region_checksum(mc) &&
(_od_base = mapinfo->map_region(od)) != NULL &&
mapinfo->verify_region_checksum(od) &&
(image_alignment == (size_t)max_alignment()) &&
mapinfo->validate_classpath_entry_table()) {
// Success (no need to do anything)
@ -1089,6 +1113,7 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
if (_rw_base != NULL) mapinfo->unmap_region(rw);
if (_md_base != NULL) mapinfo->unmap_region(md);
if (_mc_base != NULL) mapinfo->unmap_region(mc);
if (_od_base != NULL) mapinfo->unmap_region(od);
#ifndef _WINDOWS
// Release the entire mapped region
shared_rs.release();

@ -132,6 +132,7 @@ class MetaspaceShared : AllStatic {
// Used only during dumping.
static SharedMiscRegion _md;
static SharedMiscRegion _mc;
static SharedMiscRegion _od;
public:
enum {
vtbl_list_size = DEFAULT_VTBL_LIST_SIZE,
@ -148,7 +149,10 @@ class MetaspaceShared : AllStatic {
max_strings = 2, // max number of string regions in string space
num_non_strings = 4, // number of non-string regions
first_string = num_non_strings, // index of first string region
n_regions = max_strings + num_non_strings // total number of regions
// The optional data region is the last region.
// Currently it only contains class file data.
od = max_strings + num_non_strings,
n_regions = od + 1 // total number of regions
};
// Accessor functions to save shared space created for metadata, which has
@ -222,9 +226,10 @@ class MetaspaceShared : AllStatic {
static int count_class(const char* classlist_file);
static void estimate_regions_size() NOT_CDS_RETURN;
// Allocate a block of memory from the "mc" or "md" regions.
// Allocate a block of memory from the "mc", "md", or "od" regions.
static char* misc_code_space_alloc(size_t num_bytes) { return _mc.alloc(num_bytes); }
static char* misc_data_space_alloc(size_t num_bytes) { return _md.alloc(num_bytes); }
static char* optional_data_space_alloc(size_t num_bytes) { return _od.alloc(num_bytes); }
static address cds_i2i_entry_code_buffers(size_t total_size);
@ -243,5 +248,9 @@ class MetaspaceShared : AllStatic {
assert(DumpSharedSpaces, "used during dumping only");
return &_md;
}
static SharedMiscRegion* optional_data_region() {
assert(DumpSharedSpaces, "used during dumping only");
return &_od;
}
};
#endif // SHARE_VM_MEMORY_METASPACESHARED_HPP

@ -41,6 +41,7 @@
#include "memory/heapInspection.hpp"
#include "memory/iterator.inline.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp"
#include "oops/fieldStreams.hpp"
@ -1972,11 +1973,6 @@ void InstanceKlass::remove_unshareable_info() {
m->remove_unshareable_info();
}
// cached_class_file might be pointing to a malloc'ed buffer allocated by
// event-based tracing code at CDS dump time. It's not usable at runtime
// so let's clear it.
set_cached_class_file(NULL);
// do array classes also.
array_klasses_do(remove_unshareable_in_class);
}
@ -2070,6 +2066,7 @@ void InstanceKlass::release_C_heap_structures(InstanceKlass* ik) {
}
void InstanceKlass::release_C_heap_structures() {
assert(!this->is_shared(), "should not be called for a shared class");
// Can't release the constant pool here because the constant pool can be
// deallocated separately from the InstanceKlass for default methods and
@ -3653,6 +3650,15 @@ Method* InstanceKlass::method_with_orig_idnum(int idnum, int version) {
}
#if INCLUDE_JVMTI
JvmtiCachedClassFileData* InstanceKlass::get_cached_class_file() {
if (MetaspaceShared::is_in_shared_space(_cached_class_file)) {
// Ignore the archived class stream data
return NULL;
} else {
return _cached_class_file;
}
}
jint InstanceKlass::get_cached_class_file_len() {
return VM_RedefineClasses::get_cached_class_file_len(_cached_class_file);
}
@ -3660,4 +3666,15 @@ jint InstanceKlass::get_cached_class_file_len() {
unsigned char * InstanceKlass::get_cached_class_file_bytes() {
return VM_RedefineClasses::get_cached_class_file_bytes(_cached_class_file);
}
#if INCLUDE_CDS
JvmtiCachedClassFileData* InstanceKlass::get_archived_class_data() {
assert(this->is_shared(), "class should be shared");
if (MetaspaceShared::is_in_shared_space(_cached_class_file)) {
return _cached_class_file;
} else {
return NULL;
}
}
#endif
#endif

@ -783,7 +783,7 @@ public:
void set_cached_class_file(JvmtiCachedClassFileData *data) {
_cached_class_file = data;
}
JvmtiCachedClassFileData * get_cached_class_file() { return _cached_class_file; }
JvmtiCachedClassFileData * get_cached_class_file();
jint get_cached_class_file_len();
unsigned char * get_cached_class_file_bytes();
@ -795,6 +795,13 @@ public:
return _jvmti_cached_class_field_map;
}
#if INCLUDE_CDS
void set_archived_class_data(JvmtiCachedClassFileData* data) {
_cached_class_file = data;
}
JvmtiCachedClassFileData * get_archived_class_data();
#endif // INCLUDE_CDS
#else // INCLUDE_JVMTI
static void purge_previous_versions(InstanceKlass* ik) { return; };

@ -282,6 +282,12 @@ void report_untested(const char* file, int line, const char* message) {
}
void report_out_of_shared_space(SharedSpaceType shared_space) {
if (shared_space == SharedOptional) {
// The estimated shared_optional_space size is large enough
// for all class bytes. It should not run out of space.
ShouldNotReachHere();
}
static const char* name[] = {
"shared read only space",
"shared read write space",

@ -271,7 +271,8 @@ enum SharedSpaceType {
SharedReadOnly,
SharedReadWrite,
SharedMiscData,
SharedMiscCode
SharedMiscCode,
SharedOptional
};
void report_out_of_shared_space(SharedSpaceType space_type);

@ -0,0 +1,115 @@
/*
* Copyright (c) 2016, 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.
*/
import java.io.IOException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import jdk.test.lib.process.OutputAnalyzer;
// This class contains common test utilities for CDS testing
public class CDSTestUtils {
// check result of 'dump' operation
public static void checkDump(OutputAnalyzer output, String... extraMatches)
throws Exception {
output.shouldContain("Loading classes to share");
output.shouldHaveExitValue(0);
for (String match : extraMatches) {
output.shouldContain(match);
}
}
// check the output for indication that mapping of the archive failed
public static boolean isUnableToMap(OutputAnalyzer output) {
String outStr = output.getOutput();
if ((output.getExitValue() == 1) && (
outStr.contains("Unable to reserve shared space at required address") ||
outStr.contains("Unable to map ReadOnly shared space at required address") ||
outStr.contains("Unable to map ReadWrite shared space at required address") ||
outStr.contains("Unable to map MiscData shared space at required address") ||
outStr.contains("Unable to map MiscCode shared space at required address") ||
outStr.contains("Unable to map shared string space at required address") ||
outStr.contains("Could not allocate metaspace at a compatible address") ||
outStr.contains("Unable to allocate shared string space: range is not within java heap") ))
{
return true;
}
return false;
}
// check result of 'exec' operation, that is when JVM is run using the archive
public static void checkExec(OutputAnalyzer output, String... extraMatches) throws Exception {
if (isUnableToMap(output)) {
System.out.println("Unable to map shared archive: test did not complete; assumed PASS");
return;
}
output.shouldContain("sharing");
output.shouldHaveExitValue(0);
for (String match : extraMatches) {
output.shouldContain(match);
}
}
// get the file object for the test artifact
private static File getTestArtifactFile(String prefix, String name) {
File dir = new File(System.getProperty("test.classes", "."));
return new File(dir, prefix + name);
}
// create file containing the specified class list
public static File makeClassList(String testCaseName, String classes[])
throws Exception {
File classList = getTestArtifactFile(testCaseName, "test.classlist");
FileOutputStream fos = new FileOutputStream(classList);
PrintStream ps = new PrintStream(fos);
addToClassList(ps, classes);
ps.close();
fos.close();
return classList;
}
private static void addToClassList(PrintStream ps, String classes[])
throws IOException
{
if (classes != null) {
for (String s : classes) {
ps.println(s);
}
}
}
}

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, 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.
*/
public class Implementor implements Interface {
public static void main(String[] args) {
System.out.println("Implementor: entering main()");
test();
}
public static void test() {
// from interface
(new Implementor()).printString();
// from implementor
System.out.println(TransformUtil.ChildCheckPattern +
TransformUtil.BeforePattern);
}
}

@ -0,0 +1,31 @@
/*
* Copyright (c) 2016, 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.
*/
public interface Interface {
public static final String stringToBeTransformed =
TransformUtil.ParentCheckPattern + TransformUtil.BeforePattern;
default void printString() {
System.out.println(stringToBeTransformed);
}
}

@ -0,0 +1,40 @@
/*
* Copyright (c) 2016, 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.
*/
public class SubClass extends SuperClazz {
public static void main(String[] args) {
System.out.println("SubClass: entering main()");
test();
}
public static void test() {
// The line below will be used to check for successful class transformation
System.out.println(TransformUtil.ChildCheckPattern +
TransformUtil.BeforePattern);
(new SubClass()).callParent();
}
private void callParent() {
super.testParent();
}
}

@ -0,0 +1,32 @@
/*
* Copyright (c) 2016, 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.
*/
public class SuperClazz {
public static void testParent() {
System.out.println("SuperClazz: entering testParent()");
// The line below will be used to check for successful class transformation
System.out.println(TransformUtil.ParentCheckPattern + TransformUtil.BeforePattern);
}
}

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016, 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 Entry - a single entry in a test table
// that defines a test case
// See TransformRelatedClasses.java for more details
public class TestEntry {
int testCaseId;
boolean transformParent;
boolean transformChild;
boolean isParentExpectedShared;
boolean isChildExpectedShared;
public TestEntry(int testCaseId,
boolean transformParent, boolean transformChild,
boolean isParentExpectedShared, boolean isChildExpectedShared) {
this.testCaseId = testCaseId;
this.transformParent = transformParent;
this.transformChild = transformChild;
this.isParentExpectedShared = isParentExpectedShared;
this.isChildExpectedShared = isChildExpectedShared;
}
}

@ -0,0 +1,50 @@
/*
* Copyright (c) 2016, 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 Exercise initial transformation (ClassFileLoadHook)
* with CDS with Interface/Implementor pair
* @library /test/lib /runtime/SharedArchiveFile /testlibrary/jvmti
* @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
* @requires vm.flavor != "minimal"
* @modules java.base/jdk.internal.misc
* jdk.jartool/sun.tools.jar
* java.management
* java.instrument
* @build TransformUtil TransformerAgent Interface Implementor
* @run main/othervm TransformRelatedClasses Interface Implementor
*/
// Clarification on @requires declarations:
// CDS is not supported w/o the use of Compressed OOPs
// JVMTI's ClassFileLoadHook is not supported under minimal VM
// This test class uses TransformRelatedClasses to do its work.
// The goal of this test is to exercise transformation of related interface
// and its implementor in combination with CDS.
// The transformation is done via ClassFileLoadHook mechanism.
// Both superclass and subclass reside in the shared archive.
// The test consists of 4 test cases where transformation is applied
// to an interface and an implementor in a combinatorial manner.
// Please see TransformRelatedClasses.java for details.

@ -0,0 +1,179 @@
/*
* Copyright (c) 2016, 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.
*/
// This is the main test class for testing transformation of related classes
// in combination with CDS, to ensure these features work well together.
// The relationships that can be tested using this test class are:
// superclass/subclass, and interface/implementor relationships.
//
// The test uses combinatorial approach.
// For details on test table and test cases see main() method in this class.
//
// This test consists of multiple classes for better flexibility and reuse,
// and also relies on certain common utility code.
// Here are the details on the structure of the test
//
// Structure of the test:
// TransformRelatedClasses -- common main test driver
// The TransformRelatedClasses is invoked from test driver classes:
// TransformInterfaceAndImplementor, TransformSuperAndSubClasses
// It is responsible for preparing test artifacts (test jar, agent jar
// and the shared archive), running test cases and checking the results.
// The following test classes below are launched in a sub-process with use
// of shared archive:
// SuperClazz, SubClass -- super/sub class pair under test
// Interface, Implementor -- classes under test
// This test will transform these classes, based on the test case data,
// by changing a predefined unique string in each class.
// For more details, see the test classes' code and comments.
//
// Other related classes:
// TestEntry - a class representing a single test case, as test entry in the table
// TransformTestCommon - common methods for transformation test cases
//
// Other utility/helper classes and files used in this test:
// TransformerAgent - an agent that is used when JVM-under-test is executed
// to transform specific strings inside specified classes
// TransformerAgent.mf - accompanies transformer agent
// CDSTestUtils - Test Utilities common to all CDS tests
import java.io.File;
import java.util.ArrayList;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
public class TransformRelatedClasses {
static final String archiveName = "./TransformRelatedClasses.jsa";
static String agentClasses[] = {
"TransformerAgent",
"TransformerAgent$SimpleTransformer",
"TransformUtil"
};
String parent;
String child;
String[] testClasses = new String[2];
String[] testNames = new String[2];
String testJar;
String agentJar;
private static void log(String msg) {
System.out.println("TransformRelatedClasses: " + msg);
}
// This class is intended to test 2 parent-child relationships:
// 1. Base Class (parent) and Derived Class (child)
// 2. Interface (parent) and Implementor (child)
// Parameters to main(): parent, child
public static void main(String args[]) throws Exception {
TransformRelatedClasses test = new TransformRelatedClasses(args[0], args[1]);
test.prepare();
// Test Table
// TestEntry: (testCaseId, transformParent, tranformChild,
// isParentExpectedShared, isChildExpectedShared)
ArrayList<TestEntry> testTable = new ArrayList<>();
// base case - no tranformation - all expected to be shared
testTable.add(new TestEntry(0, false, false, true, true));
// transform parent only - both parent and child should not be shared
testTable.add(new TestEntry(1, true, false, false, false));
// transform parent and child - both parent and child should not be shared
testTable.add(new TestEntry(2, true, true, false, false));
// transform child only - parent should still be shared, but not child
testTable.add(new TestEntry(3, false, true, true, false));
// run the tests
for (TestEntry entry : testTable) {
test.runTest(entry);
}
}
public TransformRelatedClasses(String parent, String child) {
log("Constructor: parent = " + parent + ", child = " + child);
this.parent = parent;
this.child = child;
testClasses[0] = parent;
testClasses[1] = child;
testNames[0] = parent.replace('.', '/');
testNames[1] = child.replace('.', '/');
}
// same test jar and archive can be used for all test cases
private void prepare() throws Exception {
// create agent jar
// Agent is the same for all test cases
String pathToManifest = "../../../../testlibrary/jvmti/TransformerAgent.mf";
agentJar = ClassFileInstaller.writeJar("TransformerAgent.jar",
ClassFileInstaller.Manifest.fromSourceFile(pathToManifest),
agentClasses);
// create a test jar
testJar =
ClassFileInstaller.writeJar(parent + "-" + child + ".jar",
testClasses);
// create an archive
File classList = CDSTestUtils.makeClassList("transform-" + parent,
testNames);
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
"-Xbootclasspath/a:" + testJar,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:ExtraSharedClassListFile=" +
classList.getPath(),
"-XX:SharedArchiveFile=" + archiveName,
"-XX:+PrintSharedSpaces",
"-Xshare:dump");
OutputAnalyzer out = new OutputAnalyzer(pb.start());
CDSTestUtils.checkDump(out);
}
private void runTest(TestEntry entry) throws Exception {
log("runTest(): testCaseId = " + entry.testCaseId);
// execute with archive
String agentParam = "-javaagent:" + agentJar + "=" +
TransformTestCommon.getAgentParams(entry, parent, child);
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
"-Xbootclasspath/a:" + testJar,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:SharedArchiveFile=" + archiveName,
"-Xlog:class+load=info",
"-Xshare:on", "-showversion",
agentParam, child);
OutputAnalyzer out = new OutputAnalyzer(pb.start());
TransformTestCommon.checkResults(entry, out, parent, child);
}
}

@ -0,0 +1,51 @@
/*
* Copyright (c) 2016, 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 Exercise initial transformation (ClassFileLoadHook)
* with CDS with SubClass and SuperClass
* @library /test/lib /runtime/SharedArchiveFile /testlibrary/jvmti
* @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
* @requires vm.flavor != "minimal"
* @modules java.base/jdk.internal.misc
* jdk.jartool/sun.tools.jar
* java.management
* java.instrument
* @build TransformUtil TransformerAgent SubClass SuperClazz
* @run main/othervm TransformRelatedClasses SuperClazz SubClass
*/
// Clarification on @requires declarations:
// CDS is not supported w/o the use of Compressed OOPs
// JVMTI's ClassFileLoadHook is not supported under minimal VM
// This test class uses TransformRelatedClasses to do its work.
// The goal of this test is to exercise transformation of related superclass
// and subclass in combination with CDS.
// The transformation is done via ClassFileLoadHook mechanism.
// Both superclass and subclass reside in the shared archive.
// The test consists of 4 test cases where transformation is applied
// to a parent and child in combinatorial manner.
// Please see TransformRelatedClasses.java for details.

@ -0,0 +1,52 @@
/*
* Copyright (c) 2016, 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 Exercise initial transformation (ClassFileLoadHook)
* with CDS with SubClass and SuperClass, each lives in own separate package
* @library /test/lib /runtime/SharedArchiveFile /testlibrary/jvmti
* @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
* @requires vm.flavor != "minimal"
* @modules java.base/jdk.internal.misc
* jdk.jartool/sun.tools.jar
* java.management
* java.instrument
* @build TransformUtil TransformerAgent SubClass SuperClazz
* @compile myPkg2/SubClass.java myPkg1/SuperClazz.java
* @run main/othervm TransformRelatedClasses myPkg1.SuperClazz myPkg2.SubClass
*/
// Clarification on @requires declarations:
// CDS is not supported w/o the use of Compressed OOPs
// JVMTI's ClassFileLoadHook is not supported under minimal VM
// This test class uses TransformRelatedClasses to do its work.
// The goal of this test is to exercise transformation of related superclass
// and subclass in combination with CDS; each class lives in its own package.
// The transformation is done via ClassFileLoadHook mechanism.
// Both superclass and subclass reside in the shared archive.
// The test consists of 4 test cases where transformation is applied
// to a parent and child in combinatorial manner.
// Please see TransformRelatedClasses.java for details.

@ -0,0 +1,114 @@
/*
* Copyright (c) 2016, 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.
*/
import jdk.test.lib.process.OutputAnalyzer;
// This class contains methods common to all transformation test cases
public class TransformTestCommon {
// get parameters to an agent depending on the test case
// these parameters will instruct the agent which classes should be
// transformed
public static String getAgentParams(TestEntry entry,
String parent, String child) {
if (entry.transformParent && entry.transformChild)
return parent + "," + child;
if (entry.transformParent)
return parent;
if (entry.transformChild)
return child;
return "";
}
private static void checkTransformationResults(TestEntry entry,
OutputAnalyzer out)
throws Exception {
if (entry.transformParent)
out.shouldContain(TransformUtil.ParentCheckPattern +
TransformUtil.AfterPattern);
if (entry.transformChild)
out.shouldContain(TransformUtil.ChildCheckPattern +
TransformUtil.AfterPattern);
}
private static void checkSharingByClass(TestEntry entry, OutputAnalyzer out,
String parent, String child)
throws Exception {
String parentSharedMatch = parent + " source: shared objects file";
String childSharedMatch = child + " source: shared objects file";
if (entry.isParentExpectedShared)
out.shouldContain(parentSharedMatch);
else
out.shouldNotContain(parentSharedMatch);
if (entry.isChildExpectedShared)
out.shouldContain(childSharedMatch);
else
out.shouldNotContain(childSharedMatch);
}
// Both parent and child classes should be passed to ClassFileTransformer.transform()
// exactly once.
private static void checkTransformationCounts(TestEntry entry, OutputAnalyzer out,
String parent, String child)
throws Exception {
String patternBase = "TransformerAgent: SimpleTransformer called for: ";
out.shouldContain(patternBase + child + "@1");
out.shouldContain(patternBase + parent + "@1");
out.shouldNotContain(patternBase + child + "@2");
out.shouldNotContain(patternBase + parent + "@2");
}
public static void checkResults(TestEntry entry, OutputAnalyzer out,
String parent, String child)
throws Exception {
// If we were not able to map an archive,
// then do not perform other checks, since
// there was no sharing at all
if (CDSTestUtils.isUnableToMap(out))
return;
String childVmName = child.replace('.', '/');
String parentVmName = parent.replace('.', '/');
CDSTestUtils.checkExec(out);
checkTransformationCounts(entry, out, parentVmName, childVmName);
checkTransformationResults(entry, out);
checkSharingByClass(entry, out, parent, child);
}
}

@ -0,0 +1,33 @@
/*
* Copyright (c) 2016, 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.
*/
package myPkg1;
public class SuperClazz {
public static void testParent() {
System.out.println("SuperClazz: entering testParent()");
// The line below will be used to check for successful class transformation
System.out.println("parent-transform-check: this-should-be-transformed");
}
}

@ -0,0 +1,56 @@
/*
* Copyright (c) 2016, 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.
*/
package myPkg2;
import myPkg1.SuperClazz;
public class SubClass extends SuperClazz {
public static void main(String[] args) {
System.out.println("SubClass: entering main()");
test();
}
public static void test() {
// The line below will be used to check for successful class transformation
System.out.println("child-transform-check: this-should-be-transformed");
(new SubClass()).callParent();
// Get the system packages, which should contain myPkg1 and myPkag2
Package[] pkgs = Package.getPackages();
for (int i = 0; i < pkgs.length; i++) {
if (pkgs[i].getName().equals("myPkg1")) {
for (int j = 0; j < pkgs.length; j++) {
if (pkgs[j].getName().equals("myPkg2")) {
return; // found myPkg1 & myPkg1
}
}
}
}
throw new RuntimeException("Missing system package");
}
private void callParent() {
super.testParent();
}
}

@ -0,0 +1,75 @@
/*
* Copyright (c) 2016, 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.
*/
public class TransformUtil {
public static final String BeforePattern = "this-should-be-transformed";
public static final String AfterPattern = "this-has-been--transformed";
public static final String ParentCheckPattern = "parent-transform-check: ";
public static final String ChildCheckPattern = "child-transform-check: ";
/**
* @return the number of occurrences of the <code>from</code> string that
* have been replaced.
*/
public static int replace(byte buff[], String from, String to) {
if (to.length() != from.length()) {
throw new RuntimeException("bad strings");
}
byte f[] = asciibytes(from);
byte t[] = asciibytes(to);
byte f0 = f[0];
int numReplaced = 0;
int max = buff.length - f.length;
for (int i = 0; i < max; ) {
if (buff[i] == f0 && replace(buff, f, t, i)) {
i += f.length;
numReplaced++;
} else {
i++;
}
}
return numReplaced;
}
public static boolean replace(byte buff[], byte f[], byte t[], int i) {
for (int x = 0; x < f.length; x++) {
if (buff[x+i] != f[x]) {
return false;
}
}
for (int x = 0; x < f.length; x++) {
buff[x+i] = t[x];
}
return true;
}
static byte[] asciibytes(String s) {
byte b[] = new byte[s.length()];
for (int i = 0; i < b.length; i++) {
b[i] = (byte)s.charAt(i);
}
return b;
}
}

@ -0,0 +1,100 @@
/*
* Copyright (c) 2016, 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.
*/
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.HashMap;
// This is a test utility class used to transform
// specified classes via initial transformation (ClassFileLoadHook).
// Names of classes to be transformed are supplied as arguments,
// the phrase to be transformed is a hard-coded predefined
// fairly unique phrase.
public class TransformerAgent {
private static String[] classesToTransform;
private static void log(String msg) {
System.out.println("TransformerAgent: " + msg);
}
// arguments are comma-separated list of classes to transform
public static void premain(String agentArguments, Instrumentation instrumentation) {
log("premain() is called, arguments = " + agentArguments);
classesToTransform = agentArguments.split(",");
instrumentation.addTransformer(new SimpleTransformer(), /*canRetransform=*/true);
}
public static void agentmain(String args, Instrumentation inst) throws Exception {
log("agentmain() is called");
premain(args, inst);
}
static class SimpleTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined,
ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
log("SimpleTransformer called for: " + name + "@" + incrCounter(name));
if (!shouldTransform(name))
return null;
log("transforming: class name = " + name);
int nrOfReplacements = TransformUtil.replace(buffer, TransformUtil.BeforePattern,
TransformUtil.AfterPattern);
log("replaced the string, nrOfReplacements = " + nrOfReplacements);
return buffer;
}
// Check class name pattern, since test should only transform certain classes
private static boolean shouldTransform(String name) {
for (String match : classesToTransform) {
if (name.matches(match)) {
log("shouldTransform: match-found, match = " + match);
return true;
}
}
return false;
}
}
static HashMap<String, Integer> counterMap = new HashMap<>();
static Integer incrCounter(String className) {
Integer i = counterMap.get(className);
if (i == null) {
i = new Integer(1);
} else {
i = new Integer(i.intValue() + 1);
}
counterMap.put(className, i);
return i;
}
}

@ -0,0 +1,5 @@
Manifest-Version: 1.0
Premain-Class: TransformerAgent
Agent-Class: TransformerAgent
Can-Retransform-Classes: true
Can-Redefine-Classes: false