# HG changeset patch # User jiangli # Date 1474247448 14400 # Node ID dbd59c1da63629a8ace6827e0b20441297c9a11d # Parent 2ce2f1c582ca0ac57c11235db00fa03e92deecf5 8078644: CDS needs to support JVMTI CFLH Summary: Support posting CLFH for shared classes. Tests are contributed by Misha Seledtsov. Reviewed-by: iklam, coleenp, acorn, dcubed, sspitsyn diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/classfile/klassFactory.cpp --- a/hotspot/src/share/vm/classfile/klassFactory.cpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/classfile/klassFactory.cpp Sun Sep 18 21:10:48 2016 -0400 @@ -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 @@ const InstanceKlass* host_klass, GrowableArray* 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 @@ 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; } diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/classfile/klassFactory.hpp --- a/hotspot/src/share/vm/classfile/klassFactory.hpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/classfile/klassFactory.hpp Sun Sep 18 21:10:48 2016 -0400 @@ -75,6 +75,12 @@ const InstanceKlass* host_klass, GrowableArray* 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 diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/classfile/systemDictionary.cpp --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Sun Sep 18 21:10:48 2016 -0400 @@ -1210,16 +1210,12 @@ 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 @@ 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 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. diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/memory/filemap.cpp --- a/hotspot/src/share/vm/memory/filemap.cpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/memory/filemap.cpp Sun Sep 18 21:10:48 2016 -0400 @@ -649,7 +649,7 @@ // 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"); diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/memory/filemap.hpp --- a/hotspot/src/share/vm/memory/filemap.hpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/memory/filemap.hpp Sun Sep 18 21:10:48 2016 -0400 @@ -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 @@ 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. diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/memory/metaspace.cpp --- a/hotspot/src/share/vm/memory/metaspace.cpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/memory/metaspace.cpp Sun Sep 18 21:10:48 2016 -0400 @@ -3172,36 +3172,28 @@ 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(); - } + 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."); - } + } else { + assert(!mapinfo->is_open() && !UseSharedSpaces, + "archive file not closed or shared spaces not disabled."); } } #endif // INCLUDE_CDS diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/memory/metaspaceShared.cpp --- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Sun Sep 18 21:10:48 2016 -0400 @@ -65,6 +65,7 @@ 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 @@ 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); + + // 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); - // Now split into misc sections. + // 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 @@ GrowableArray *_class_promote_order; VirtualSpace _md_vs; VirtualSpace _mc_vs; + VirtualSpace _od_vs; GrowableArray *_string_regions; public: @@ -598,15 +608,19 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ 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 @@ 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(); diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/memory/metaspaceShared.hpp --- a/hotspot/src/share/vm/memory/metaspaceShared.hpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp Sun Sep 18 21:10:48 2016 -0400 @@ -132,6 +132,7 @@ // 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 @@ 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 @@ 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 @@ 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 diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/oops/instanceKlass.cpp --- a/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Sun Sep 18 21:10:48 2016 -0400 @@ -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 @@ 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() { + 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 @@ } #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 @@ 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 diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/oops/instanceKlass.hpp --- a/hotspot/src/share/vm/oops/instanceKlass.hpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Sun Sep 18 21:10:48 2016 -0400 @@ -783,7 +783,7 @@ 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 @@ 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; }; diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/utilities/debug.cpp --- a/hotspot/src/share/vm/utilities/debug.cpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/utilities/debug.cpp Sun Sep 18 21:10:48 2016 -0400 @@ -282,6 +282,12 @@ } 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", diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/src/share/vm/utilities/debug.hpp --- a/hotspot/src/share/vm/utilities/debug.hpp Tue Sep 06 13:01:27 2016 +0200 +++ b/hotspot/src/share/vm/utilities/debug.hpp Sun Sep 18 21:10:48 2016 -0400 @@ -271,7 +271,8 @@ SharedReadOnly, SharedReadWrite, SharedMiscData, - SharedMiscCode + SharedMiscCode, + SharedOptional }; void report_out_of_shared_space(SharedSpaceType space_type); diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/CDSTestUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/CDSTestUtils.java Sun Sep 18 21:10:48 2016 -0400 @@ -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); + } + } + } + +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/Implementor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/Implementor.java Sun Sep 18 21:10:48 2016 -0400 @@ -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); + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/Interface.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/Interface.java Sun Sep 18 21:10:48 2016 -0400 @@ -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); + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/SubClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/SubClass.java Sun Sep 18 21:10:48 2016 -0400 @@ -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(); + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/SuperClazz.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/SuperClazz.java Sun Sep 18 21:10:48 2016 -0400 @@ -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); + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TestEntry.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TestEntry.java Sun Sep 18 21:10:48 2016 -0400 @@ -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; + } +} + diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformInterfaceAndImplementor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformInterfaceAndImplementor.java Sun Sep 18 21:10:48 2016 -0400 @@ -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. diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformRelatedClasses.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformRelatedClasses.java Sun Sep 18 21:10:48 2016 -0400 @@ -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 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); + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformSuperAndSubClasses.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformSuperAndSubClasses.java Sun Sep 18 21:10:48 2016 -0400 @@ -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. diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformSuperSubTwoPckgs.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformSuperSubTwoPckgs.java Sun Sep 18 21:10:48 2016 -0400 @@ -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. diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformTestCommon.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformTestCommon.java Sun Sep 18 21:10:48 2016 -0400 @@ -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); + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/myPkg1/SuperClazz.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/myPkg1/SuperClazz.java Sun Sep 18 21:10:48 2016 -0400 @@ -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"); + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/myPkg2/SubClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/myPkg2/SubClass.java Sun Sep 18 21:10:48 2016 -0400 @@ -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(); + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/testlibrary/jvmti/TransformUtil.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/testlibrary/jvmti/TransformUtil.java Sun Sep 18 21:10:48 2016 -0400 @@ -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 from 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; + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/testlibrary/jvmti/TransformerAgent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/testlibrary/jvmti/TransformerAgent.java Sun Sep 18 21:10:48 2016 -0400 @@ -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 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; + } +} diff -r 2ce2f1c582ca -r dbd59c1da636 hotspot/test/testlibrary/jvmti/TransformerAgent.mf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/testlibrary/jvmti/TransformerAgent.mf Sun Sep 18 21:10:48 2016 -0400 @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Premain-Class: TransformerAgent +Agent-Class: TransformerAgent +Can-Retransform-Classes: true +Can-Redefine-Classes: false