# HG changeset patch # User jprovino # Date 1474404335 0 # Node ID 0eb0a2183ff28931d4bbd9b5e8e6767931307abf # Parent b4276ec89d0ddb607ba1fde602957a5f2b697d32# Parent 474076f73ba136baaf951af4434033ee9c312d7c Merge diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/classfile/classLoader.cpp --- a/hotspot/src/share/vm/classfile/classLoader.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/classfile/classLoader.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -85,6 +85,7 @@ typedef jzentry* (JNICALL *GetNextEntry_t)(jzfile *zip, jint n); typedef jboolean (JNICALL *ZipInflateFully_t)(void *inBuf, jlong inLen, void *outBuf, jlong outLen, char **pmsg); typedef jint (JNICALL *Crc32_t)(jint crc, const jbyte *buf, jint len); +typedef void (JNICALL *FreeEntry_t)(jzfile *zip, jzentry *entry); static ZipOpen_t ZipOpen = NULL; static ZipClose_t ZipClose = NULL; @@ -95,6 +96,7 @@ static canonicalize_fn_t CanonicalizeEntry = NULL; static ZipInflateFully_t ZipInflateFully = NULL; static Crc32_t Crc32 = NULL; +static FreeEntry_t FreeEntry = NULL; // Entry points for jimage.dll for loading jimage file entries @@ -150,6 +152,7 @@ GrowableArray* ClassLoader::_boot_modules_array = NULL; GrowableArray* ClassLoader::_platform_modules_array = NULL; SharedPathsMiscInfo* ClassLoader::_shared_paths_misc_info = NULL; +int ClassLoader::_num_patch_mod_prefixes = 0; #endif // helper routines @@ -319,6 +322,20 @@ FREE_C_HEAP_ARRAY(char, _zip_name); } +bool ClassPathZipEntry::stream_exists(const char* name) { + // enable call to C land + JavaThread* thread = JavaThread::current(); + ThreadToNativeFromVM ttn(thread); + // check whether zip archive contains name + jint name_len, filesize; + jzentry* entry = (*FindEntry)(_zip, name, &filesize, &name_len); + if (entry != NULL) { + (*FreeEntry)(_zip, entry); + return true; + } + return false; +} + u1* ClassPathZipEntry::open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS) { // enable call to C land JavaThread* thread = JavaThread::current(); @@ -640,7 +657,7 @@ struct stat st; if (os::stat(path, &st) == 0) { - if ((st.st_mode & S_IFREG) != S_IFREG) { // is directory + if ((st.st_mode & S_IFMT) != S_IFREG) { // is not a regular file if (!os::dir_is_empty(path)) { tty->print_cr("Error: non-empty directory '%s'", path); exit_with_path_failure("CDS allows only empty directories in archived classpaths", NULL); @@ -693,8 +710,6 @@ GrowableArray* patch_mod_args = Arguments::get_patch_mod_prefix(); int num_of_entries = patch_mod_args->length(); - assert(!DumpSharedSpaces, "DumpSharedSpaces not supported with --patch-module"); - assert(!UseSharedSpaces, "UseSharedSpaces not supported with --patch-module"); // Set up the boot loader's _patch_mod_entries list _patch_mod_entries = new (ResourceObj::C_HEAP, mtModule) GrowableArray(num_of_entries, true); @@ -851,7 +866,7 @@ bool is_boot_append, TRAPS) { JavaThread* thread = JavaThread::current(); ClassPathEntry* new_entry = NULL; - if ((st->st_mode & S_IFREG) == S_IFREG) { + if ((st->st_mode & S_IFMT) == S_IFREG) { ResourceMark rm(thread); // Regular file, should be a zip or jimage file // Canonicalized filename @@ -914,7 +929,7 @@ // check for a regular file struct stat st; if (os::stat(path, &st) == 0) { - if ((st.st_mode & S_IFREG) == S_IFREG) { + if ((st.st_mode & S_IFMT) == S_IFREG) { char canonical_path[JVM_MAXPATHLEN]; if (get_canonical_path(path, canonical_path, JVM_MAXPATHLEN)) { char* error_msg = NULL; @@ -1068,6 +1083,7 @@ GetNextEntry = CAST_TO_FN_PTR(GetNextEntry_t, os::dll_lookup(handle, "ZIP_GetNextEntry")); ZipInflateFully = CAST_TO_FN_PTR(ZipInflateFully_t, os::dll_lookup(handle, "ZIP_InflateFully")); Crc32 = CAST_TO_FN_PTR(Crc32_t, os::dll_lookup(handle, "ZIP_CRC32")); + FreeEntry = CAST_TO_FN_PTR(FreeEntry_t, os::dll_lookup(handle, "ZIP_FreeEntry")); // ZIP_Close is not exported on Windows in JDK5.0 so don't abort if ZIP_Close is NULL if (ZipOpen == NULL || FindEntry == NULL || ReadEntry == NULL || @@ -1358,7 +1374,7 @@ if (!Universe::is_module_initialized() && !ModuleEntryTable::javabase_defined() && mod_entry == NULL) { - mod_entry = ModuleEntryTable::javabase_module(); + mod_entry = ModuleEntryTable::javabase_moduleEntry(); } // The module must be a named module @@ -1395,6 +1411,57 @@ return NULL; } +#if INCLUDE_CDS +// The following function is only used during CDS dump time. +// It checks if a class can be found in the jar entries of the _patch_mod_entries. +// It does not support non-jar entries. +bool ClassLoader::is_in_patch_module(const char* const file_name) { + assert(DumpSharedSpaces, "dump time only"); + if (_patch_mod_entries == NULL) { + return false; + } + + int num_of_entries = _patch_mod_entries->length(); + char* class_module_name = NULL; + ResourceMark rm; + const char *pkg_name = package_from_name(file_name); + // Using the jimage to obtain the class' module name. + // The ModuleEntryTable cannot be used at this point during dump time + // because the module system hasn't been initialized yet. + if (pkg_name != NULL) { + JImageFile *jimage = _jrt_entry->jimage(); + class_module_name = (char*)(*JImagePackageToModule)(jimage, pkg_name); + } + + if (class_module_name == NULL) { + return false; + } + + // Loop through all the patch module entries looking for module + for (int i = 0; i < num_of_entries; i++) { + ModuleClassPathList* module_cpl = _patch_mod_entries->at(i); + Symbol* module_cpl_name = module_cpl->module_name(); + + if (strcmp(module_cpl_name->as_C_string(), class_module_name) == 0) { + // Class' module has been located, attempt to locate + // the class from the module's ClassPathEntry list. + ClassPathEntry* e = module_cpl->module_first_entry(); + while (e != NULL) { + if (e->is_jar_file()) { + if (e->stream_exists(file_name)) { + return true; + } else { + e = e->next(); + } + } + } + } + } + + return false; +} +#endif // INCLUDE_CDS + instanceKlassHandle ClassLoader::load_class(Symbol* name, bool search_append_only, TRAPS) { assert(name != NULL, "invariant"); assert(THREAD->is_Java_thread(), "must be a JavaThread"); @@ -1420,8 +1487,8 @@ // If DumpSharedSpaces is true boot loader visibility boundaries are set to: // - [jimage] + [_first_append_entry to _last_append_entry] (all path entries). - // No --patch-module entries or exploded module builds are included since CDS - // is not supported if --patch-module or exploded module builds are used. + // If a class is found in the --patch-module entries, the class will not be included in the + // CDS archive. Also, CDS is not supported if exploded module builds are used. // // If search_append_only is true, boot loader visibility boundaries are // set to be _first_append_entry to the end. This includes: @@ -1444,8 +1511,17 @@ // found within its module specification, the search should continue to Load Attempt #2. // Note: The --patch-module entries are never searched if the boot loader's // visibility boundary is limited to only searching the append entries. - if (_patch_mod_entries != NULL && !search_append_only && !DumpSharedSpaces) { - stream = search_module_entries(_patch_mod_entries, class_name, file_name, CHECK_NULL); + if (_patch_mod_entries != NULL && !search_append_only) { + if (!DumpSharedSpaces) { + stream = search_module_entries(_patch_mod_entries, class_name, file_name, CHECK_NULL); + } else { +#if INCLUDE_CDS + if (is_in_patch_module(file_name)) { + tty->print_cr("Preload Warning: Skip archiving class %s found in --patch-module entry", class_name); + return NULL; + } +#endif + } } // Load Attempt #2: [jimage | exploded build] @@ -1596,8 +1672,57 @@ } #if INCLUDE_CDS +// Capture all the --patch-module entries specified during CDS dump time. +// It also captures the non-existing path(s) and the required file(s) during inspecting +// the entries. +void ClassLoader::setup_patch_mod_path() { + assert(DumpSharedSpaces, "only used with -Xshare:dump"); + ResourceMark rm; + GrowableArray* patch_mod_args = Arguments::get_patch_mod_prefix(); + if (patch_mod_args != NULL) { + int num_of_entries = patch_mod_args->length(); + for (int i = 0; i < num_of_entries; i++) { + const char* module_name = (patch_mod_args->at(i))->module_name(); + const char* module_path = (patch_mod_args->at(i))->path_string(); + int path_len = (int)strlen(module_path); + int name_len = (int)strlen(module_name); + int buf_len = name_len + path_len + 2; // add 2 for the '=' and NULL terminator + int end = 0; + char* buf = NEW_C_HEAP_ARRAY(char, buf_len, mtInternal); + // Iterate over the module's class path entries + for (int start = 0; start < path_len; start = end) { + while (module_path[end] && module_path[end] != os::path_separator()[0]) { + end++; + } + strncpy(buf, &module_path[start], end - start); + buf[end - start] = '\0'; + struct stat st; + if (os::stat(buf, &st) != 0) { + // File not found + _shared_paths_misc_info->add_nonexist_path(buf); + } else { + if ((st.st_mode & S_IFMT) != S_IFREG) { // is not a regular file + vm_exit_during_initialization( + "--patch-module requires a regular file during dumping", buf); + } else { + _shared_paths_misc_info->add_required_file(buf); + } + } + while (module_path[end] == os::path_separator()[0]) { + end++; + } + }; + jio_snprintf(buf, buf_len, "%s=%s", module_name, module_path); + _shared_paths_misc_info->add_patch_mod_classpath((const char*)buf); + _num_patch_mod_prefixes++; + FREE_C_HEAP_ARRAY(char, buf); + } + } +} + void ClassLoader::initialize_shared_path() { if (DumpSharedSpaces) { + setup_patch_mod_path(); ClassLoaderExt::setup_search_paths(); _shared_paths_misc_info->write_jint(0); // see comments in SharedPathsMiscInfo::check() } @@ -1708,7 +1833,7 @@ if (jb_module == NULL) { vm_exit_during_initialization("Unable to create ModuleEntry for java.base"); } - ModuleEntryTable::set_javabase_module(jb_module); + ModuleEntryTable::set_javabase_moduleEntry(jb_module); } } diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/classfile/classLoader.hpp --- a/hotspot/src/share/vm/classfile/classLoader.hpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/classfile/classLoader.hpp Tue Sep 20 20:45:35 2016 +0000 @@ -69,6 +69,7 @@ // Attempt to locate file_name through this class path entry. // Returns a class file parsing stream if successfull. virtual ClassFileStream* open_stream(const char* name, TRAPS) = 0; + virtual bool stream_exists(const char* name) = 0; // Debugging NOT_PRODUCT(virtual void compile_the_world(Handle loader, TRAPS) = 0;) }; @@ -83,6 +84,7 @@ JImageFile* jimage() const { return NULL; } ClassPathDirEntry(const char* dir); ClassFileStream* open_stream(const char* name, TRAPS); + bool stream_exists(const char* name) { return false; } // Debugging NOT_PRODUCT(void compile_the_world(Handle loader, TRAPS);) }; @@ -126,6 +128,7 @@ ClassFileStream* open_stream(const char* name, TRAPS); void contents_do(void f(const char* name, void* context), void* context); bool is_multiple_versioned(TRAPS) NOT_CDS_RETURN_(false); + bool stream_exists(const char* name); // Debugging NOT_PRODUCT(void compile_the_world(Handle loader, TRAPS);) }; @@ -145,6 +148,7 @@ ClassPathImageEntry(JImageFile* jimage, const char* name); ~ClassPathImageEntry(); ClassFileStream* open_stream(const char* name, TRAPS); + bool stream_exists(const char* name) { return false; } // Debugging NOT_PRODUCT(void compile_the_world(Handle loader, TRAPS);) @@ -255,6 +259,7 @@ // Info used by CDS CDS_ONLY(static SharedPathsMiscInfo * _shared_paths_misc_info;) + CDS_ONLY(static int _num_patch_mod_prefixes;) // Initialization: // - setup the boot loader's system class path @@ -427,6 +432,9 @@ static void initialize_module_loader_map(JImageFile* jimage); static s2 classloader_type(Symbol* class_name, ClassPathEntry* e, int classpath_index, TRAPS); + static bool is_in_patch_module(const char* const file_name); + static void setup_patch_mod_path(); // Only when -Xshare:dump + static int num_patch_mod_prefixes() { return _num_patch_mod_prefixes; } #endif static void trace_class_path(const char* msg, const char* name = NULL); diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/classfile/javaClasses.cpp --- a/hotspot/src/share/vm/classfile/javaClasses.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -773,6 +773,41 @@ InstanceKlass::cast(k())->do_local_static_fields(&initialize_static_field, mirror, CHECK); } +// Set the java.lang.reflect.Module module field in the java_lang_Class mirror +void java_lang_Class::set_mirror_module_field(KlassHandle k, Handle mirror, Handle module, TRAPS) { + if (module.is_null()) { + // During startup, the module may be NULL only if java.base has not been defined yet. + // Put the class on the fixup_module_list to patch later when the java.lang.reflect.Module + // for java.base is known. + assert(!Universe::is_module_initialized(), "Incorrect java.lang.reflect.Module pre module system initialization"); + MutexLocker m1(Module_lock, THREAD); + // Keep list of classes needing java.base module fixup + if (!ModuleEntryTable::javabase_defined()) { + if (fixup_module_field_list() == NULL) { + GrowableArray* list = + new (ResourceObj::C_HEAP, mtModule) GrowableArray(500, true); + set_fixup_module_field_list(list); + } + k->class_loader_data()->inc_keep_alive(); + fixup_module_field_list()->push(k()); + } else { + // java.base was defined at some point between calling create_mirror() + // and obtaining the Module_lock, patch this particular class with java.base. + ModuleEntry *javabase_entry = ModuleEntryTable::javabase_moduleEntry(); + assert(javabase_entry != NULL && javabase_entry->module() != NULL, + "Setting class module field, java.base should be defined"); + Handle javabase_handle(THREAD, JNIHandles::resolve(javabase_entry->module())); + set_module(mirror(), javabase_handle()); + } + } else { + assert(Universe::is_module_initialized() || + (ModuleEntryTable::javabase_defined() && + (module() == JNIHandles::resolve(ModuleEntryTable::javabase_moduleEntry()->module()))), + "Incorrect java.lang.reflect.Module specification while creating mirror"); + set_module(mirror(), module()); + } +} + void java_lang_Class::create_mirror(KlassHandle k, Handle class_loader, Handle module, Handle protection_domain, TRAPS) { assert(k->java_mirror() == NULL, "should only assign mirror once"); @@ -835,25 +870,13 @@ set_class_loader(mirror(), class_loader()); // set the module field in the java_lang_Class instance - // This may be null during bootstrap but will get fixed up later on. - set_module(mirror(), module()); + set_mirror_module_field(k, mirror, module, THREAD); // Setup indirection from klass->mirror last // after any exceptions can happen during allocations. if (!k.is_null()) { k->set_java_mirror(mirror()); } - - // Keep list of classes needing java.base module fixup. - if (!ModuleEntryTable::javabase_defined()) { - if (fixup_module_field_list() == NULL) { - GrowableArray* list = - new (ResourceObj::C_HEAP, mtModule) GrowableArray(500, true); - set_fixup_module_field_list(list); - } - k->class_loader_data()->inc_keep_alive(); - fixup_module_field_list()->push(k()); - } } else { if (fixup_mirror_list() == NULL) { GrowableArray* list = diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/classfile/javaClasses.hpp --- a/hotspot/src/share/vm/classfile/javaClasses.hpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Tue Sep 20 20:45:35 2016 +0000 @@ -219,6 +219,7 @@ static void set_class_loader(oop java_class, oop class_loader); static void set_component_mirror(oop java_class, oop comp_mirror); static void initialize_mirror_fields(KlassHandle k, Handle mirror, Handle protection_domain, TRAPS); + static void set_mirror_module_field(KlassHandle K, Handle mirror, Handle module, TRAPS); public: static void compute_offsets(); diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/classfile/moduleEntry.cpp --- a/hotspot/src/share/vm/classfile/moduleEntry.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/classfile/moduleEntry.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -92,7 +92,7 @@ // read java.base. If either of these conditions // hold, readability has been established. if (!this->is_named() || - (m == ModuleEntryTable::javabase_module())) { + (m == ModuleEntryTable::javabase_moduleEntry())) { return true; } @@ -358,16 +358,27 @@ } // Set java.lang.reflect.Module, version and location for java.base - ModuleEntry* jb_module = javabase_module(); + ModuleEntry* jb_module = javabase_moduleEntry(); assert(jb_module != NULL, "java.base ModuleEntry not defined"); - jb_module->set_module(boot_loader_data->add_handle(module_handle)); jb_module->set_version(version); jb_module->set_location(location); + // Once java.base's ModuleEntry _module field is set with the known + // java.lang.reflect.Module, java.base is considered "defined" to the VM. + jb_module->set_module(boot_loader_data->add_handle(module_handle)); + // Store pointer to the ModuleEntry for java.base in the java.lang.reflect.Module object. java_lang_reflect_Module::set_module_entry(module_handle(), jb_module); + + // Patch any previously loaded classes' module field with java.base's java.lang.reflect.Module. + patch_javabase_entries(module_handle); } +// Within java.lang.Class instances there is a java.lang.reflect.Module field +// that must be set with the defining module. During startup, prior to java.base's +// definition, classes needing their module field set are added to the fixup_module_list. +// Their module field is set once java.base's java.lang.reflect.Module is known to the VM. void ModuleEntryTable::patch_javabase_entries(Handle module_handle) { + assert(Module_lock->owned_by_self(), "should have the Module_lock"); if (module_handle.is_null()) { fatal("Unable to patch the module field of classes loaded prior to java.base's definition, invalid java.lang.reflect.Module"); } @@ -389,9 +400,7 @@ for (int i = 0; i < list_length; i++) { Klass* k = list->at(i); assert(k->is_klass(), "List should only hold classes"); - Thread* THREAD = Thread::current(); - KlassHandle kh(THREAD, k); - java_lang_Class::fixup_module_field(kh, module_handle); + java_lang_Class::fixup_module_field(KlassHandle(k), module_handle); k->class_loader_data()->dec_keep_alive(); } diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/classfile/moduleEntry.hpp --- a/hotspot/src/share/vm/classfile/moduleEntry.hpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/classfile/moduleEntry.hpp Tue Sep 20 20:45:35 2016 +0000 @@ -78,11 +78,11 @@ _must_walk_reads = false; } - Symbol* name() const { return literal(); } - void set_name(Symbol* n) { set_literal(n); } + Symbol* name() const { return literal(); } + void set_name(Symbol* n) { set_literal(n); } - jobject module() const { return _module; } - void set_module(jobject j) { _module = j; } + jobject module() const { return _module; } + void set_module(jobject j) { _module = j; } // The shared ProtectionDomain reference is set once the VM loads a shared class // originated from the current Module. The referenced ProtectionDomain object is @@ -217,13 +217,13 @@ // Special handling for unnamed module, one per class loader's ModuleEntryTable void create_unnamed_module(ClassLoaderData* loader_data); - ModuleEntry* unnamed_module() { return _unnamed_module; } + ModuleEntry* unnamed_module() { return _unnamed_module; } // Special handling for java.base - static ModuleEntry* javabase_module() { return _javabase_module; } - static void set_javabase_module(ModuleEntry* java_base) { _javabase_module = java_base; } - static bool javabase_defined() { return ((_javabase_module != NULL) && - (_javabase_module->module() != NULL)); } + static ModuleEntry* javabase_moduleEntry() { return _javabase_module; } + static void set_javabase_moduleEntry(ModuleEntry* java_base) { _javabase_module = java_base; } + static bool javabase_defined() { return ((_javabase_module != NULL) && + (_javabase_module->module() != NULL)); } static void finalize_javabase(Handle module_handle, Symbol* version, Symbol* location); static void patch_javabase_entries(Handle module_handle); diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/classfile/modules.cpp --- a/hotspot/src/share/vm/classfile/modules.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/classfile/modules.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -206,7 +206,7 @@ assert(pkg_list->length() == 0 || package_table != NULL, "Bad package_table"); // Ensure java.base's ModuleEntry has been created - assert(ModuleEntryTable::javabase_module() != NULL, "No ModuleEntry for java.base"); + assert(ModuleEntryTable::javabase_moduleEntry() != NULL, "No ModuleEntry for java.base"); bool duplicate_javabase = false; { @@ -226,7 +226,7 @@ for (int x = 0; x < pkg_list->length(); x++) { // Some of java.base's packages were added early in bootstrapping, ignore duplicates. if (package_table->lookup_only(pkg_list->at(x)) == NULL) { - pkg = package_table->locked_create_entry_or_null(pkg_list->at(x), ModuleEntryTable::javabase_module()); + pkg = package_table->locked_create_entry_or_null(pkg_list->at(x), ModuleEntryTable::javabase_moduleEntry()); assert(pkg != NULL, "Unable to create a java.base package entry"); } // Unable to have a GrowableArray of TempNewSymbol. Must decrement the refcount of @@ -255,9 +255,6 @@ log_trace(modules)("define_javabase_module(): creation of package %s for module java.base", (pkg_list->at(x))->as_C_string()); } - - // Patch any previously loaded classes' module field with java.base's jlr.Module. - ModuleEntryTable::patch_javabase_entries(module_handle); } void Modules::define_module(jobject module, jstring version, diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp --- a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -86,6 +86,9 @@ case REQUIRED: out->print("Expecting that file %s must exist and is not altered", path); break; + case PATCH_MOD: + out->print("Expecting --patch-module=%s", path); + break; default: ShouldNotReachHere(); } @@ -146,6 +149,9 @@ // But we want it to not exist -> fail return fail("File must not exist"); } + if ((st.st_mode & S_IFMT) != S_IFREG) { + return fail("Did not get a regular file as expected."); + } time_t timestamp; long filesize; @@ -161,7 +167,26 @@ } } break; - + case PATCH_MOD: + { + GrowableArray* patch_mod_args = Arguments::get_patch_mod_prefix(); + if (patch_mod_args != NULL) { + int num_of_entries = patch_mod_args->length(); + for (int i = 0; i < num_of_entries; i++) { + const char* module_name = (patch_mod_args->at(i))->module_name(); + const char* path_string = (patch_mod_args->at(i))->path_string(); + size_t n = strlen(module_name); + // path contains the module name, followed by '=', and one or more entries. + // E.g.: "java.base=foo" or "java.naming=dir1:dir2:dir3" + if ((strncmp(module_name, path, n) != 0) || + (path[n] != '=') || + (strcmp(path + n + 1, path_string) != 0)) { + return fail("--patch-module mismatch, path not found in run time: ", path); + } + } + } + } + break; default: return fail("Corrupted archive file header"); } diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp --- a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp Tue Sep 20 20:45:35 2016 +0000 @@ -104,10 +104,28 @@ add_path(path, NON_EXIST); } + // The path must exist and have required size and modification time + void add_required_file(const char* path) { + add_path(path, REQUIRED); + + struct stat st; + if (os::stat(path, &st) != 0) { + assert(0, "sanity"); +#if INCLUDE_CDS + ClassLoader::exit_with_path_failure("failed to os::stat(%s)", path); // should not happen +#endif + } + write_time(st.st_mtime); + write_long(st.st_size); + } + // The path must exist, and must contain exactly files/dirs void add_boot_classpath(const char* path) { add_path(path, BOOT); } + void add_patch_mod_classpath(const char* path) { + add_path(path, PATCH_MOD); + } int write_jint(jint num) { write(&num, sizeof(num)); return 0; @@ -129,7 +147,8 @@ enum { BOOT = 1, NON_EXIST = 2, - REQUIRED = 3 + REQUIRED = 3, + PATCH_MOD = 4 }; virtual const char* type_name(int type) { @@ -137,6 +156,7 @@ case BOOT: return "BOOT"; case NON_EXIST: return "NON_EXIST"; case REQUIRED: return "REQUIRED"; + case PATCH_MOD: return "PATCH_MOD"; default: ShouldNotReachHere(); return "?"; } } diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/gc/cms/parNewGeneration.cpp --- a/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -1366,22 +1366,25 @@ return false; } assert(prefix != NULL && prefix != BUSY, "Error"); - size_t i = 1; oop cur = prefix; - while (i < objsFromOverflow && cur->klass_or_null() != NULL) { - i++; cur = cur->list_ptr_from_klass(); + for (size_t i = 1; i < objsFromOverflow; ++i) { + oop next = cur->list_ptr_from_klass(); + if (next == NULL) break; + cur = next; } + assert(cur != NULL, "Loop postcondition"); // Reattach remaining (suffix) to overflow list - if (cur->klass_or_null() == NULL) { + oop suffix = cur->list_ptr_from_klass(); + if (suffix == NULL) { // Write back the NULL in lieu of the BUSY we wrote // above and it is still the same value. if (_overflow_list == BUSY) { (void) Atomic::cmpxchg_ptr(NULL, &_overflow_list, BUSY); } } else { - assert(cur->klass_or_null() != (Klass*)(address)BUSY, "Error"); - oop suffix = cur->list_ptr_from_klass(); // suffix will be put back on global list + assert(suffix != BUSY, "Error"); + // suffix will be put back on global list cur->set_klass_to_list_ptr(NULL); // break off suffix // It's possible that the list is still in the empty(busy) state // we left it in a short while ago; in that case we may be @@ -1401,8 +1404,10 @@ // Too bad, someone else got in in between; we'll need to do a splice. // Find the last item of suffix list oop last = suffix; - while (last->klass_or_null() != NULL) { - last = last->list_ptr_from_klass(); + while (true) { + oop next = last->list_ptr_from_klass(); + if (next == NULL) break; + last = next; } // Atomically prepend suffix to current overflow list observed_overflow_list = _overflow_list; diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/gc/g1/g1ConcurrentMark.cpp --- a/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -285,7 +285,7 @@ return false; } - Copy::conjoint_oops_atomic(ptr_arr, new_chunk->data, OopsPerChunk); + Copy::conjoint_memory_atomic(ptr_arr, new_chunk->data, OopsPerChunk * sizeof(oop)); add_chunk_to_chunk_list(new_chunk); @@ -299,7 +299,7 @@ return false; } - Copy::conjoint_oops_atomic(cur->data, ptr_arr, OopsPerChunk); + Copy::conjoint_memory_atomic(cur->data, ptr_arr, OopsPerChunk * sizeof(oop)); add_chunk_to_free_list(cur); return true; diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/gc/g1/heapRegion.cpp --- a/hotspot/src/share/vm/gc/g1/heapRegion.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/gc/g1/heapRegion.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -353,35 +353,6 @@ } HeapWord* -HeapRegion::object_iterate_mem_careful(MemRegion mr, - ObjectClosure* cl) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - // We used to use "block_start_careful" here. But we're actually happy - // to update the BOT while we do this... - HeapWord* cur = block_start(mr.start()); - mr = mr.intersection(used_region()); - if (mr.is_empty()) return NULL; - // Otherwise, find the obj that extends onto mr.start(). - - assert(cur <= mr.start() - && (oop(cur)->klass_or_null() == NULL || - cur + oop(cur)->size() > mr.start()), - "postcondition of block_start"); - oop obj; - while (cur < mr.end()) { - obj = oop(cur); - if (obj->klass_or_null() == NULL) { - // Ran into an unparseable point. - return cur; - } else if (!g1h->is_obj_dead(obj)) { - cl->do_object(obj); - } - cur += block_size(cur); - } - return NULL; -} - -HeapWord* HeapRegion:: oops_on_card_seq_iterate_careful(MemRegion mr, FilterOutOfRegionClosure* cl, diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/gc/g1/heapRegion.hpp --- a/hotspot/src/share/vm/gc/g1/heapRegion.hpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/gc/g1/heapRegion.hpp Tue Sep 20 20:45:35 2016 +0000 @@ -653,17 +653,6 @@ } } - // Requires that "mr" be entirely within the region. - // Apply "cl->do_object" to all objects that intersect with "mr". - // If the iteration encounters an unparseable portion of the region, - // or if "cl->abort()" is true after a closure application, - // terminate the iteration and return the address of the start of the - // subregion that isn't done. (The two can be distinguished by querying - // "cl->abort()".) Return of "NULL" indicates that the iteration - // completed. - HeapWord* - object_iterate_mem_careful(MemRegion mr, ObjectClosure* cl); - // filter_young: if true and the region is a young region then we // skip the iteration. // card_ptr: if not NULL, and we decide that the card is not young diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/gc/shared/workgroup.cpp --- a/hotspot/src/share/vm/gc/shared/workgroup.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/gc/shared/workgroup.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -472,23 +472,21 @@ } bool SequentialSubTasksDone::is_task_claimed(uint& t) { - uint* n_claimed_ptr = &_n_claimed; - t = *n_claimed_ptr; + t = _n_claimed; while (t < _n_tasks) { - jint res = Atomic::cmpxchg(t+1, n_claimed_ptr, t); + jint res = Atomic::cmpxchg(t+1, &_n_claimed, t); if (res == (jint)t) { return false; } - t = *n_claimed_ptr; + t = res; } return true; } bool SequentialSubTasksDone::all_tasks_completed() { - uint* n_completed_ptr = &_n_completed; - uint complete = *n_completed_ptr; + uint complete = _n_completed; while (true) { - uint res = Atomic::cmpxchg(complete+1, n_completed_ptr, complete); + uint res = Atomic::cmpxchg(complete+1, &_n_completed, complete); if (res == complete) { break; } diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/gc/shared/workgroup.hpp --- a/hotspot/src/share/vm/gc/shared/workgroup.hpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/gc/shared/workgroup.hpp Tue Sep 20 20:45:35 2016 +0000 @@ -318,9 +318,9 @@ // enumeration type. class SubTasksDone: public CHeapObj { - uint* _tasks; + volatile uint* _tasks; uint _n_tasks; - uint _threads_completed; + volatile uint _threads_completed; #ifdef ASSERT volatile uint _claimed; #endif @@ -363,11 +363,11 @@ class SequentialSubTasksDone : public StackObj { protected: uint _n_tasks; // Total number of tasks available. - uint _n_claimed; // Number of tasks claimed. + volatile uint _n_claimed; // Number of tasks claimed. // _n_threads is used to determine when a sub task is done. // See comments on SubTasksDone::_n_threads uint _n_threads; // Total number of parallel threads. - uint _n_completed; // Number of completed threads. + volatile uint _n_completed; // Number of completed threads. void clear(); diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/memory/filemap.cpp --- a/hotspot/src/share/vm/memory/filemap.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/memory/filemap.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -179,6 +179,7 @@ _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; _classpath_entry_table = mapinfo->_classpath_entry_table; _classpath_entry_size = mapinfo->_classpath_entry_size; + _num_patch_mod_prefixes = ClassLoader::num_patch_mod_prefixes(); // The following fields are for sanity checks for whether this archive // will function correctly with this JVM and the bootclasspath it's @@ -911,11 +912,6 @@ return false; } - if (Arguments::get_patch_mod_prefix() != NULL) { - FileMapInfo::fail_continue("The shared archive file cannot be used with --patch-module."); - return false; - } - if (!Arguments::has_jimage()) { FileMapInfo::fail_continue("The shared archive file cannot be used with an exploded module build."); return false; @@ -952,6 +948,23 @@ return false; } + // Check if there is a mismatch in --patch-module entry counts between dump time and run time. + // More checks will be performed on individual --patch-module entry in the + // SharedPathsMiscInfo::check() function. + GrowableArray* patch_mod_args = Arguments::get_patch_mod_prefix(); + if (patch_mod_args != NULL) { + if (_num_patch_mod_prefixes == 0) { + FileMapInfo::fail_stop("--patch-module found in run time but none was specified in dump time"); + } + if (patch_mod_args->length() != _num_patch_mod_prefixes) { + FileMapInfo::fail_stop("mismatched --patch-module entry counts between dump time and run time"); + } + } else { + if (_num_patch_mod_prefixes > 0) { + FileMapInfo::fail_stop("--patch-module specified in dump time but none was specified in run time"); + } + } + return true; } diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/memory/filemap.hpp --- a/hotspot/src/share/vm/memory/filemap.hpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/memory/filemap.hpp Tue Sep 20 20:45:35 2016 +0000 @@ -155,6 +155,7 @@ // loading failures during runtime. int _classpath_entry_table_size; size_t _classpath_entry_size; + int _num_patch_mod_prefixes; // number of --patch-module entries SharedClassPathEntry* _classpath_entry_table; char* region_addr(int idx); diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/oops/instanceKlass.cpp --- a/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -2247,8 +2247,8 @@ // the java.base module. If a non-java.base package is erroneously placed // in the java.base module it will be caught later when java.base // is defined by ModuleEntryTable::verify_javabase_packages check. - assert(ModuleEntryTable::javabase_module() != NULL, "java.base module is NULL"); - _package_entry = loader_data->packages()->lookup(pkg_name, ModuleEntryTable::javabase_module()); + assert(ModuleEntryTable::javabase_moduleEntry() != NULL, "java.base module is NULL"); + _package_entry = loader_data->packages()->lookup(pkg_name, ModuleEntryTable::javabase_moduleEntry()); } else { assert(loader_data->modules()->unnamed_module() != NULL, "unnamed module is NULL"); _package_entry = loader_data->packages()->lookup(pkg_name, diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/oops/klass.cpp --- a/hotspot/src/share/vm/oops/klass.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/oops/klass.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -530,7 +530,7 @@ InstanceKlass* ik = (InstanceKlass*) k; module_entry = ik->module(); } else { - module_entry = ModuleEntryTable::javabase_module(); + module_entry = ModuleEntryTable::javabase_moduleEntry(); } // Obtain java.lang.reflect.Module, if available Handle module_handle(THREAD, ((module_entry != NULL) ? JNIHandles::resolve(module_entry->module()) : (oop)NULL)); diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/oops/typeArrayKlass.cpp --- a/hotspot/src/share/vm/oops/typeArrayKlass.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/oops/typeArrayKlass.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -72,7 +72,7 @@ null_loader_data->add_class(ak); // Call complete_create_array_klass after all instance variables have been initialized. - complete_create_array_klass(ak, ak->super(), ModuleEntryTable::javabase_module(), CHECK_NULL); + complete_create_array_klass(ak, ak->super(), ModuleEntryTable::javabase_moduleEntry(), CHECK_NULL); return ak; } @@ -347,7 +347,7 @@ // A TypeArrayKlass is an array of a primitive type, its defining module is java.base ModuleEntry* TypeArrayKlass::module() const { - return ModuleEntryTable::javabase_module(); + return ModuleEntryTable::javabase_moduleEntry(); } PackageEntry* TypeArrayKlass::package() const { diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/prims/jvm.cpp --- a/hotspot/src/share/vm/prims/jvm.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/prims/jvm.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -562,8 +562,8 @@ } Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream)); - return StackWalk::moreFrames(stackStream_h, mode, anchor, frame_count, - start_index, frames_array_h, THREAD); + return StackWalk::fetchNextBatch(stackStream_h, mode, anchor, frame_count, + start_index, frames_array_h, THREAD); JVM_END JVM_ENTRY(void, JVM_ToStackTraceElement(JNIEnv *env, jobject frame, jobject stack)) diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/prims/stackwalk.cpp --- a/hotspot/src/share/vm/prims/stackwalk.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/prims/stackwalk.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -37,42 +37,47 @@ #include "utilities/globalDefinitions.hpp" // setup and cleanup actions -void JavaFrameStream::setup_magic_on_entry(objArrayHandle frames_array) { +void BaseFrameStream::setup_magic_on_entry(objArrayHandle frames_array) { frames_array->obj_at_put(magic_pos, _thread->threadObj()); _anchor = address_value(); assert(check_magic(frames_array), "invalid magic"); } -bool JavaFrameStream::check_magic(objArrayHandle frames_array) { +bool BaseFrameStream::check_magic(objArrayHandle frames_array) { oop m1 = frames_array->obj_at(magic_pos); jlong m2 = _anchor; if (m1 == _thread->threadObj() && m2 == address_value()) return true; return false; } -bool JavaFrameStream::cleanup_magic_on_exit(objArrayHandle frames_array) { +bool BaseFrameStream::cleanup_magic_on_exit(objArrayHandle frames_array) { bool ok = check_magic(frames_array); frames_array->obj_at_put(magic_pos, NULL); _anchor = 0L; return ok; } -// Returns JavaFrameStream for the current stack being traversed. +JavaFrameStream::JavaFrameStream(JavaThread* thread, int mode) + : BaseFrameStream(thread), _vfst(thread) { + _need_method_info = StackWalk::need_method_info(mode); +} + +// Returns the BaseFrameStream for the current stack being traversed. // // Parameters: // thread Current Java thread. // magic Magic value used for each stack walking // frames_array User-supplied buffers. The 0th element is reserved -// to this JavaFrameStream to use +// for this BaseFrameStream to use // -JavaFrameStream* JavaFrameStream::from_current(JavaThread* thread, jlong magic, +BaseFrameStream* BaseFrameStream::from_current(JavaThread* thread, jlong magic, objArrayHandle frames_array) { assert(thread != NULL && thread->is_Java_thread(), ""); oop m1 = frames_array->obj_at(magic_pos); if (m1 != thread->threadObj()) return NULL; if (magic == 0L) return NULL; - JavaFrameStream* stream = (JavaFrameStream*) (intptr_t) magic; + BaseFrameStream* stream = (BaseFrameStream*) (intptr_t) magic; if (!stream->is_valid_in(thread, frames_array)) return NULL; return stream; } @@ -85,7 +90,7 @@ // // Parameters: // mode Restrict which frames to be decoded. -// JavaFrameStream stream of javaVFrames +// BaseFrameStream stream of frames // max_nframes Maximum number of frames to be filled. // start_index Start index to the user-supplied buffers. // frames_array Buffer to store Class or StackFrame in, starting at start_index. @@ -96,7 +101,7 @@ // // Returns the number of frames whose information was transferred into the buffers. // -int StackWalk::fill_in_frames(jlong mode, JavaFrameStream& stream, +int StackWalk::fill_in_frames(jlong mode, BaseFrameStream& stream, int max_nframes, int start_index, objArrayHandle frames_array, int& end_index, TRAPS) { @@ -110,7 +115,6 @@ int frames_decoded = 0; for (; !stream.at_end(); stream.next()) { Method* method = stream.method(); - int bci = stream.bci(); if (method == NULL) continue; @@ -129,35 +133,42 @@ int index = end_index++; if (TraceStackWalk) { tty->print(" %d: frame method: ", index); method->print_short_name(); - tty->print_cr(" bci=%d", bci); + tty->print_cr(" bci=%d", stream.bci()); } + if (!need_method_info(mode) && get_caller_class(mode) && + index == start_index && method->caller_sensitive()) { + ResourceMark rm(THREAD); + THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), + err_msg("StackWalker::getCallerClass called from @CallerSensitive %s method", + method->name_and_sig_as_C_string())); + } // fill in StackFrameInfo and initialize MemberName - if (live_frame_info(mode)) { - assert (use_frames_array(mode), "Bad mode for get live frame"); - Handle stackFrame(frames_array->obj_at(index)); - fill_live_stackframe(stackFrame, method, bci, stream.java_frame(), CHECK_0); - } else if (need_method_info(mode)) { - assert (use_frames_array(mode), "Bad mode for get stack frame"); - Handle stackFrame(frames_array->obj_at(index)); - fill_stackframe(stackFrame, method, bci); - } else { - assert (use_frames_array(mode) == false, "Bad mode for filling in Class object"); - if (get_caller_class(mode) && index == start_index && method->caller_sensitive()) { - ResourceMark rm(THREAD); - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), - err_msg("StackWalker::getCallerClass called from @CallerSensitive %s method", - method->name_and_sig_as_C_string())); - } - - frames_array->obj_at_put(index, method->method_holder()->java_mirror()); - } + stream.fill_frame(index, frames_array, method, CHECK_0); if (++frames_decoded >= max_nframes) break; } return frames_decoded; } -static oop create_primitive_value_instance(StackValueCollection* values, int i, TRAPS) { +// Fill in the LiveStackFrameInfo at the given index in frames_array +void LiveFrameStream::fill_frame(int index, objArrayHandle frames_array, + const methodHandle& method, TRAPS) { + Handle stackFrame(THREAD, frames_array->obj_at(index)); + fill_live_stackframe(stackFrame, method, CHECK); +} + +// Fill in the StackFrameInfo at the given index in frames_array +void JavaFrameStream::fill_frame(int index, objArrayHandle frames_array, + const methodHandle& method, TRAPS) { + if (_need_method_info) { + Handle stackFrame(THREAD, frames_array->obj_at(index)); + fill_stackframe(stackFrame, method); + } else { + frames_array->obj_at_put(index, method->method_holder()->java_mirror()); + } +} + +oop LiveFrameStream::create_primitive_value_instance(StackValueCollection* values, int i, TRAPS) { Klass* k = SystemDictionary::resolve_or_null(vmSymbols::java_lang_LiveStackFrameInfo(), CHECK_NULL); instanceKlassHandle ik (THREAD, k); @@ -228,7 +239,7 @@ return (instanceOop) result.get_jobject(); } -static objArrayHandle values_to_object_array(StackValueCollection* values, TRAPS) { +objArrayHandle LiveFrameStream::values_to_object_array(StackValueCollection* values, TRAPS) { objArrayHandle empty; int length = values->size(); objArrayOop array_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), @@ -243,7 +254,7 @@ return array_h; } -static objArrayHandle monitors_to_object_array(GrowableArray* monitors, TRAPS) { +objArrayHandle LiveFrameStream::monitors_to_object_array(GrowableArray* monitors, TRAPS) { int length = monitors->length(); objArrayOop array_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), length, CHECK_(objArrayHandle())); @@ -256,19 +267,19 @@ } // Fill StackFrameInfo with declaringClass and bci and initialize memberName -void StackWalk::fill_stackframe(Handle stackFrame, const methodHandle& method, int bci) { +void BaseFrameStream::fill_stackframe(Handle stackFrame, const methodHandle& method) { java_lang_StackFrameInfo::set_declaringClass(stackFrame(), method->method_holder()->java_mirror()); - java_lang_StackFrameInfo::set_method_and_bci(stackFrame(), method, bci); + java_lang_StackFrameInfo::set_method_and_bci(stackFrame(), method, bci()); } // Fill LiveStackFrameInfo with locals, monitors, and expressions -void StackWalk::fill_live_stackframe(Handle stackFrame, const methodHandle& method, - int bci, javaVFrame* jvf, TRAPS) { - fill_stackframe(stackFrame, method, bci); - if (jvf != NULL) { - StackValueCollection* locals = jvf->locals(); - StackValueCollection* expressions = jvf->expressions(); - GrowableArray* monitors = jvf->monitors(); +void LiveFrameStream::fill_live_stackframe(Handle stackFrame, + const methodHandle& method, TRAPS) { + fill_stackframe(stackFrame, method); + if (_jvf != NULL) { + StackValueCollection* locals = _jvf->locals(); + StackValueCollection* expressions = _jvf->expressions(); + GrowableArray* monitors = _jvf->monitors(); if (!locals->is_empty()) { objArrayHandle locals_h = values_to_object_array(locals, CHECK); @@ -315,15 +326,26 @@ THROW_MSG_(vmSymbols::java_lang_NullPointerException(), "frames_array is NULL", NULL); } - Klass* stackWalker_klass = SystemDictionary::StackWalker_klass(); - Klass* abstractStackWalker_klass = SystemDictionary::AbstractStackWalker_klass(); + // Setup traversal onto my stack. + if (live_frame_info(mode)) { + assert (use_frames_array(mode), "Bad mode for get live frame"); + RegisterMap regMap(jt, true); + LiveFrameStream stream(jt, ®Map); + return fetchFirstBatch(stream, stackStream, mode, skip_frames, frame_count, start_index, frames_array, CHECK_NULL); + } else { + JavaFrameStream stream(jt, mode); + return fetchFirstBatch(stream, stackStream, mode, skip_frames, frame_count, start_index, frames_array, CHECK_NULL); + } +} +oop StackWalk::fetchFirstBatch(BaseFrameStream& stream, Handle stackStream, + jlong mode, int skip_frames, int frame_count, + int start_index, objArrayHandle frames_array, TRAPS) { methodHandle m_doStackWalk(THREAD, Universe::do_stack_walk_method()); - // Setup traversal onto my stack. - RegisterMap regMap(jt, true); - JavaFrameStream stream(jt, ®Map); { + Klass* stackWalker_klass = SystemDictionary::StackWalker_klass(); + Klass* abstractStackWalker_klass = SystemDictionary::AbstractStackWalker_klass(); while (!stream.at_end()) { InstanceKlass* ik = stream.method()->method_holder(); if (ik != stackWalker_klass && @@ -341,10 +363,7 @@ // from the stack frame at depth == skip_frames. for (int n=0; n < skip_frames && !stream.at_end(); stream.next(), n++) { if (TraceStackWalk) { - tty->print(" skip "); stream.method()->print_short_name(); - tty->print_cr(" frame id: " PTR_FORMAT " pc: " PTR_FORMAT, - p2i(stream.java_frame()->fr().id()), - p2i(stream.java_frame()->fr().pc())); + tty->print(" skip "); stream.method()->print_short_name(); tty->cr(); } } } @@ -402,13 +421,13 @@ // // Returns the end index of frame filled in the buffer. // -jint StackWalk::moreFrames(Handle stackStream, jlong mode, jlong magic, - int frame_count, int start_index, - objArrayHandle frames_array, - TRAPS) +jint StackWalk::fetchNextBatch(Handle stackStream, jlong mode, jlong magic, + int frame_count, int start_index, + objArrayHandle frames_array, + TRAPS) { JavaThread* jt = (JavaThread*)THREAD; - JavaFrameStream* existing_stream = JavaFrameStream::from_current(jt, magic, frames_array); + BaseFrameStream* existing_stream = BaseFrameStream::from_current(jt, magic, frames_array); if (existing_stream == NULL) { THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: corrupted buffers", 0L); } @@ -418,7 +437,7 @@ } if (TraceStackWalk) { - tty->print_cr("StackWalk::moreFrames frame_count %d existing_stream " PTR_FORMAT " start %d frames %d", + tty->print_cr("StackWalk::fetchNextBatch frame_count %d existing_stream " PTR_FORMAT " start %d frames %d", frame_count, p2i(existing_stream), start_index, frames_array->length()); } int end_index = start_index; @@ -429,7 +448,7 @@ int count = frame_count + start_index; assert (frames_array->length() >= count, "not enough space in buffers"); - JavaFrameStream& stream = (*existing_stream); + BaseFrameStream& stream = (*existing_stream); if (!stream.at_end()) { stream.next(); // advance past the last frame decoded in previous batch if (!stream.at_end()) { diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/prims/stackwalk.hpp --- a/hotspot/src/share/vm/prims/stackwalk.hpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/prims/stackwalk.hpp Tue Sep 20 20:45:35 2016 +0000 @@ -29,31 +29,34 @@ #include "oops/oop.hpp" #include "runtime/vframe.hpp" -// -// JavaFrameStream is used by StackWalker to iterate through Java stack frames -// on the given JavaThread. -// -class JavaFrameStream : public StackObj { +// BaseFrameStream is an abstract base class for encapsulating the VM-side +// implementation of the StackWalker API. There are two concrete subclasses: +// - JavaFrameStream: +// -based on vframeStream; used in most instances +// - LiveFrameStream: +// -based on javaVFrame; used for retrieving locals/monitors/operands for +// LiveStackFrame +class BaseFrameStream : public StackObj { private: enum { magic_pos = 0 }; JavaThread* _thread; - javaVFrame* _jvf; jlong _anchor; +protected: + void fill_stackframe(Handle stackFrame, const methodHandle& method); public: - JavaFrameStream(JavaThread* thread, RegisterMap* rm) - : _thread(thread), _anchor(0L) { - _jvf = _thread->last_java_vframe(rm); - } + BaseFrameStream(JavaThread* thread) : _thread(thread), _anchor(0L) {} + + virtual void next()=0; + virtual bool at_end()=0; - javaVFrame* java_frame() { return _jvf; } - void next() { _jvf = _jvf->java_sender(); } - bool at_end() { return _jvf == NULL; } + virtual Method* method()=0; + virtual int bci()=0; - Method* method() { return _jvf->method(); } - int bci() { return _jvf->bci(); } + virtual void fill_frame(int index, objArrayHandle frames_array, + const methodHandle& method, TRAPS)=0; void setup_magic_on_entry(objArrayHandle frames_array); bool check_magic(objArrayHandle frames_array); @@ -67,35 +70,72 @@ return (jlong) castable_address(this); } - static JavaFrameStream* from_current(JavaThread* thread, jlong magic, objArrayHandle frames_array); + static BaseFrameStream* from_current(JavaThread* thread, jlong magic, objArrayHandle frames_array); +}; + +class JavaFrameStream : public BaseFrameStream { +private: + vframeStream _vfst; + bool _need_method_info; +public: + JavaFrameStream(JavaThread* thread, int mode); + + void next() { _vfst.next();} + bool at_end() { return _vfst.at_end(); } + + Method* method() { return _vfst.method(); } + int bci() { return _vfst.bci(); } + + void fill_frame(int index, objArrayHandle frames_array, + const methodHandle& method, TRAPS); +}; + +class LiveFrameStream : public BaseFrameStream { +private: + javaVFrame* _jvf; + + void fill_live_stackframe(Handle stackFrame, const methodHandle& method, TRAPS); + static oop create_primitive_value_instance(StackValueCollection* values, + int i, TRAPS); + static objArrayHandle monitors_to_object_array(GrowableArray* monitors, + TRAPS); + static objArrayHandle values_to_object_array(StackValueCollection* values, TRAPS); +public: + LiveFrameStream(JavaThread* thread, RegisterMap* rm) : BaseFrameStream(thread) { + _jvf = thread->last_java_vframe(rm); + } + + void next() { _jvf = _jvf->java_sender(); } + bool at_end() { return _jvf == NULL; } + + Method* method() { return _jvf->method(); } + int bci() { return _jvf->bci(); } + + void fill_frame(int index, objArrayHandle frames_array, + const methodHandle& method, TRAPS); }; class StackWalk : public AllStatic { private: - static int fill_in_frames(jlong mode, JavaFrameStream& stream, + static int fill_in_frames(jlong mode, BaseFrameStream& stream, int max_nframes, int start_index, objArrayHandle frames_array, int& end_index, TRAPS); - static void fill_stackframe(Handle stackFrame, const methodHandle& method, int bci); - - static void fill_live_stackframe(Handle stackFrame, const methodHandle& method, int bci, - javaVFrame* jvf, TRAPS); - static inline bool get_caller_class(int mode) { return (mode & JVM_STACKWALK_GET_CALLER_CLASS) != 0; } static inline bool skip_hidden_frames(int mode) { return (mode & JVM_STACKWALK_SHOW_HIDDEN_FRAMES) == 0; } - static inline bool need_method_info(int mode) { - return (mode & JVM_STACKWALK_FILL_CLASS_REFS_ONLY) == 0; - } static inline bool live_frame_info(int mode) { return (mode & JVM_STACKWALK_FILL_LIVE_STACK_FRAMES) != 0; } public: + static inline bool need_method_info(int mode) { + return (mode & JVM_STACKWALK_FILL_CLASS_REFS_ONLY) == 0; + } static inline bool use_frames_array(int mode) { return (mode & JVM_STACKWALK_FILL_CLASS_REFS_ONLY) == 0; } @@ -104,9 +144,12 @@ objArrayHandle frames_array, TRAPS); - static jint moreFrames(Handle stackStream, jlong mode, jlong magic, - int frame_count, int start_index, - objArrayHandle frames_array, - TRAPS); + static oop fetchFirstBatch(BaseFrameStream& stream, Handle stackStream, + jlong mode, int skip_frames, int frame_count, + int start_index, objArrayHandle frames_array, TRAPS); + + static jint fetchNextBatch(Handle stackStream, jlong mode, jlong magic, + int frame_count, int start_index, + objArrayHandle frames_array, TRAPS); }; #endif // SHARE_VM_PRIMS_STACKWALK_HPP diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/runtime/arguments.cpp --- a/hotspot/src/share/vm/runtime/arguments.cpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/runtime/arguments.cpp Tue Sep 20 20:45:35 2016 +0000 @@ -3897,10 +3897,6 @@ void Arguments::set_shared_spaces_flags() { if (DumpSharedSpaces) { - if (Arguments::get_patch_mod_prefix() != NULL) { - vm_exit_during_initialization( - "Cannot use the following option when dumping the shared archive: --patch-module"); - } if (RequireSharedSpaces) { warning("Cannot dump shared archive while using shared archive"); diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/src/share/vm/utilities/hashtable.inline.hpp --- a/hotspot/src/share/vm/utilities/hashtable.inline.hpp Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/src/share/vm/utilities/hashtable.inline.hpp Tue Sep 20 20:45:35 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, 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 @@ -79,8 +79,8 @@ template inline void HashtableBucket::set_entry(BasicHashtableEntry* l) { - // Warning: Preserve store ordering. The SystemDictionary is read - // without locks. The new SystemDictionaryEntry must be + // Warning: Preserve store ordering. The PackageEntryTable, ModuleEntryTable and + // SystemDictionary are read without locks. The new entry must be // complete before other threads can be allowed to see it // via a store to _buckets[index]. OrderAccess::release_store_ptr(&_entry, l); @@ -88,8 +88,8 @@ template inline BasicHashtableEntry* HashtableBucket::get_entry() const { - // Warning: Preserve load ordering. The SystemDictionary is read - // without locks. The new SystemDictionaryEntry must be + // Warning: Preserve load ordering. The PackageEntryTable, ModuleEntryTable and + // SystemDictionary are read without locks. The new entry must be // complete before other threads can be allowed to see it // via a store to _buckets[index]. return (BasicHashtableEntry*) OrderAccess::load_ptr_acquire(&_entry); diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/test/runtime/modules/PatchModule/PatchModuleCDS.java --- a/hotspot/test/runtime/modules/PatchModule/PatchModuleCDS.java Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/test/runtime/modules/PatchModule/PatchModuleCDS.java Tue Sep 20 20:45:35 2016 +0000 @@ -23,41 +23,83 @@ /* * @test + * @summary test that --patch-module works with CDS * @library /test/lib * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @build PatchModuleMain * @run main PatchModuleCDS */ import java.io.File; +import jdk.test.lib.InMemoryJavaCompiler; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; public class PatchModuleCDS { public static void main(String args[]) throws Throwable { - System.out.println("Test that --patch-module and -Xshare:dump are incompatibable"); - ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("--patch-module=java.naming=mods/java.naming", "-Xshare:dump"); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldContain("Cannot use the following option when dumping the shared archive: --patch-module"); - System.out.println("Test that --patch-module and -Xshare:on are incompatibable"); + // Case 1: Test that --patch-module and -Xshare:dump are compatible String filename = "patch_module.jsa"; - pb = ProcessTools.createJavaProcessBuilder( + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=" + filename, - "-Xshare:dump"); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("ro space:"); // Make sure archive got created. + "-Xshare:dump", + "--patch-module=java.naming=no/such/directory", + "-Xlog:class+path=info", + "-version"); + new OutputAnalyzer(pb.start()) + .shouldContain("ro space:"); // Make sure archive got created. + + // Case 2: Test that only jar file in --patch-module is supported for CDS dumping + // Create a class file in the module java.base. + String source = "package javax.naming.spi; " + + "public class NamingManager { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + + ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager", + InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "-Xmodule:java.naming"), + System.getProperty("test.classes")); pb = ProcessTools.createJavaProcessBuilder( "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=" + filename, - "-Xshare:on", - "--patch-module=java.naming=mods/java.naming", + "-Xshare:dump", + "--patch-module=java.base=" + System.getProperty("test.classes"), + "-Xlog:class+path=info", "-version"); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("The shared archive file cannot be used with --patch-module"); + new OutputAnalyzer(pb.start()) + .shouldContain("--patch-module requires a regular file during dumping"); - output.shouldHaveExitValue(1); + // Case 3a: Test CDS dumping with jar file in --patch-module + BasicJarBuilder.build("javanaming", "javax/naming/spi/NamingManager"); + String moduleJar = BasicJarBuilder.getTestJar("javanaming.jar"); + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=" + filename, + "-Xshare:dump", + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "-Xlog:class+path=info", + "PatchModuleMain", "javax.naming.spi.NamingManager"); + new OutputAnalyzer(pb.start()) + .shouldContain("ro space:"); // Make sure archive got created. + + // Case 3b: Test CDS run with jar file in --patch-module + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=" + filename, + "-Xshare:auto", + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "-Xlog:class+path=info", + "PatchModuleMain", "javax.naming.spi.NamingManager"); + new OutputAnalyzer(pb.start()) + .shouldContain("I pass!") + .shouldHaveExitValue(0); } } diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/test/serviceability/jdwp/AllModulesCommandTest.java --- a/hotspot/test/serviceability/jdwp/AllModulesCommandTest.java Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/test/serviceability/jdwp/AllModulesCommandTest.java Tue Sep 20 20:45:35 2016 +0000 @@ -30,7 +30,7 @@ /** * @test - * @summary Tests the modules-related JDWP commands + * @summary Tests AllModules JDWP command * @library /test/lib * @modules java.base/jdk.internal.misc * @compile AllModulesCommandTestDebuggee.java @@ -87,12 +87,8 @@ assertReply(reply); for (int i = 0; i < reply.getModulesCount(); ++i) { long modId = reply.getModuleId(i); - // For each module reported by JDWP get its name using the JDWP NAME command - // and store the reply - String modName = getModuleName(modId); - if (modName != null) { // JDWP reports unnamed modules, ignore them - jdwpModuleNames.add(modName); - } + // For each module reported by JDWP get its name using the JDWP NAME command + getModuleName(modId); // Assert the JDWP CANREAD and CLASSLOADER commands assertCanRead(modId); assertClassLoader(modId); @@ -118,10 +114,14 @@ } } - private String getModuleName(long modId) throws IOException { + private void getModuleName(long modId) throws IOException { + // Send out the JDWP NAME command and store the reply JdwpModNameReply reply = new JdwpModNameCmd(modId).send(channel); assertReply(reply); - return reply.getModuleName(); + String modName = reply.getModuleName(); + if (modName != null) { // JDWP reports unnamed modules, ignore them + jdwpModuleNames.add(modName); + } } private void assertReply(JdwpReply reply) { @@ -139,39 +139,11 @@ } private void assertClassLoader(long modId) throws IOException { - // Verify that the module classloader id is valid + // Simple assert for the CLASSLOADER command JdwpClassLoaderReply reply = new JdwpClassLoaderCmd(modId).send(channel); assertReply(reply); - long moduleClassLoader = reply.getClassLoaderId(); - assertTrue(moduleClassLoader >= 0, "bad classloader refId " + moduleClassLoader + " for module id " + modId); - - String clsModName = getModuleName(modId); - if ("java.base".equals(clsModName)) { - // For the java.base module, because there will be some loaded classes, we can verify - // that some of the loaded classes do report the java.base module as the module they belong to - assertGetModule(moduleClassLoader, modId); - } - } - - private void assertGetModule(long moduleClassLoader, long modId) throws IOException { - // Get all the visible classes for the module classloader - JdwpVisibleClassesReply visibleClasses = new JdwpVisibleClassesCmd(moduleClassLoader).send(channel); - assertReply(visibleClasses); - - boolean moduleFound = false; - for (long clsId : visibleClasses.getVisibleClasses()) { - // For each visible class get the module the class belongs to - JdwpModuleReply modReply = new JdwpModuleCmd(clsId).send(channel); - assertReply(modReply); - long clsModId = modReply.getModuleId(); - - // At least one of the visible classes should belong to our module - if (modId == clsModId) { - moduleFound = true; - break; - } - } - assertTrue(moduleFound, "None of the visible classes for the classloader of the module " + getModuleName(modId) + " reports the module as its own"); + long clId = reply.getClassLoaderId(); + assertTrue(clId >= 0, "bad classloader refId " + clId + " for module id " + modId); } } diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/test/serviceability/jdwp/JdwpCmd.java --- a/hotspot/test/serviceability/jdwp/JdwpCmd.java Tue Sep 20 10:27:51 2016 -0400 +++ b/hotspot/test/serviceability/jdwp/JdwpCmd.java Tue Sep 20 20:45:35 2016 +0000 @@ -70,6 +70,7 @@ } public final T send(JdwpChannel channel) throws IOException { + System.err.println("Sending command: " + this); channel.write(data.array(), HEADER_LEN + getDataLength()); if (reply != null) { reply.initFromStream(channel.getInputStream()); diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/test/serviceability/jdwp/JdwpModuleCmd.java --- a/hotspot/test/serviceability/jdwp/JdwpModuleCmd.java Tue Sep 20 10:27:51 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * 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. - */ - -/** - * The JDWP MODULE command - */ -public class JdwpModuleCmd extends JdwpCmd { - - public JdwpModuleCmd(long refId) { - super(19, 2, JdwpModuleReply.class, refLen()); - putRefId(refId); - } - -} diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/test/serviceability/jdwp/JdwpModuleReply.java --- a/hotspot/test/serviceability/jdwp/JdwpModuleReply.java Tue Sep 20 10:27:51 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/* - * 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.DataInputStream; -import java.io.IOException; - -/** - * The reply to the JDWP MODULE command - */ -public class JdwpModuleReply extends JdwpReply { - - private long moduleId; - - protected void parseData(DataInputStream ds) throws IOException { - moduleId = readRefId(ds); - } - - public long getModuleId() { - return moduleId; - } - -} diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/test/serviceability/jdwp/JdwpVisibleClassesCmd.java --- a/hotspot/test/serviceability/jdwp/JdwpVisibleClassesCmd.java Tue Sep 20 10:27:51 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * 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. - */ - -/** - * The JDWP VISIBLE CLASSES command - */ -public class JdwpVisibleClassesCmd extends JdwpCmd { - - public JdwpVisibleClassesCmd(long classLoaderId) { - super(1, 14, JdwpVisibleClassesReply.class, refLen()); - putRefId(classLoaderId); - } - -} diff -r b4276ec89d0d -r 0eb0a2183ff2 hotspot/test/serviceability/jdwp/JdwpVisibleClassesReply.java --- a/hotspot/test/serviceability/jdwp/JdwpVisibleClassesReply.java Tue Sep 20 10:27:51 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * 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.DataInputStream; -import java.io.IOException; -import java.util.Arrays; - -/** - * The reply to the JDWP VISIBLE CLASSES command - */ -public class JdwpVisibleClassesReply extends JdwpReply { - - private long[] visibleClasses; - - protected void parseData(DataInputStream ds) throws IOException { - int numOfClasses = ds.readInt(); - visibleClasses = new long[numOfClasses]; - for (int i = 0; i < numOfClasses; ++i) { - byte type = ds.readByte(); - long refId = readRefId(ds); - visibleClasses[i] = refId; - } - } - - public long[] getVisibleClasses() { - return Arrays.copyOf(visibleClasses, visibleClasses.length); - } - -}