8212200: assert when shared java.lang.Object is redefined by JVMTI agent

Reviewed-by: dholmes, jiangli, hseigel, lfoltan, sspitsyn
This commit is contained in:
Ioi Lam 2018-10-17 15:57:10 -07:00
parent 02966a7b67
commit 93395f6a9e
11 changed files with 375 additions and 43 deletions

@ -50,6 +50,7 @@
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/resolvedMethodTable.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/frame.inline.hpp"
@ -125,7 +126,7 @@ static void compute_offset(int &dest_offset,
if (ik == NULL) {
ResourceMark rm;
log_error(class)("Mismatch JDK version for field: %s type: %s", name_symbol->as_C_string(), signature_symbol->as_C_string());
vm_exit_during_initialization("Invalid layout of preloaded class");
vm_exit_during_initialization("Invalid layout of well-known class");
}
if (!ik->find_local_field(name_symbol, signature_symbol, &fd) || fd.is_static() != is_static) {
@ -138,7 +139,7 @@ static void compute_offset(int &dest_offset,
LogStream ls(lt.error());
ik->print_on(&ls);
#endif //PRODUCT
vm_exit_during_initialization("Invalid layout of preloaded class: use -Xlog:class+load=info to see the origin of the problem class");
vm_exit_during_initialization("Invalid layout of well-known class: use -Xlog:class+load=info to see the origin of the problem class");
}
dest_offset = fd.offset();
}
@ -151,7 +152,7 @@ static void compute_offset(int& dest_offset, InstanceKlass* ik,
if (name == NULL) {
ResourceMark rm;
log_error(class)("Name %s should be in the SymbolTable since its class is loaded", name_string);
vm_exit_during_initialization("Invalid layout of preloaded class", ik->external_name());
vm_exit_during_initialization("Invalid layout of well-known class", ik->external_name());
}
compute_offset(dest_offset, ik, name, signature_symbol, is_static);
}
@ -1196,7 +1197,7 @@ bool java_lang_Class::restore_archived_mirror(Klass *k,
Handle class_loader, Handle module,
Handle protection_domain, TRAPS) {
// Postpone restoring archived mirror until java.lang.Class is loaded. Please
// see more details in SystemDictionary::resolve_preloaded_classes().
// see more details in SystemDictionary::resolve_well_known_classes().
if (!SystemDictionary::Class_klass_loaded()) {
assert(fixup_mirror_list() != NULL, "fixup_mirror_list not initialized");
fixup_mirror_list()->push(k);
@ -4250,12 +4251,19 @@ void JavaClasses::compute_hard_coded_offsets() {
// Compute non-hard-coded field offsets of all the classes in this file
void JavaClasses::compute_offsets() {
if (UseSharedSpaces) {
return; // field offsets are loaded from archive
assert(JvmtiExport::is_early_phase() && !(JvmtiExport::should_post_class_file_load_hook() &&
JvmtiExport::has_early_class_hook_env()),
"JavaClasses::compute_offsets() must be called in early JVMTI phase.");
// None of the classes used by the rest of this function can be replaced by
// JMVTI ClassFileLoadHook.
// We are safe to use the archived offsets, which have already been restored
// by JavaClasses::serialize_offsets, without computing the offsets again.
return;
}
// We have already called the compute_offsets() of the
// BASIC_JAVA_CLASSES_DO_PART1 classes (java_lang_String and java_lang_Class)
// earlier inside SystemDictionary::resolve_preloaded_classes()
// earlier inside SystemDictionary::resolve_well_known_classes()
BASIC_JAVA_CLASSES_DO_PART2(DO_COMPUTE_OFFSETS);
// generated interpreter code wants to know about the offsets we just computed:
@ -4356,7 +4364,7 @@ int InjectedField::compute_offset() {
tty->print_cr(" name: %s, sig: %s, flags: %08x", fs.name()->as_C_string(), fs.signature()->as_C_string(), fs.access_flags().as_int());
}
#endif //PRODUCT
vm_exit_during_initialization("Invalid layout of preloaded class: use -Xlog:class+load=info to see the origin of the problem class");
vm_exit_during_initialization("Invalid layout of well-known class: use -Xlog:class+load=info to see the origin of the problem class");
return -1;
}

@ -67,7 +67,7 @@
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "oops/typeArrayKlass.hpp"
#include "prims/jvmtiEnvBase.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/resolvedMethodTable.hpp"
#include "prims/methodHandles.hpp"
#include "runtime/arguments.hpp"
@ -1489,8 +1489,7 @@ InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle
!search_only_bootloader_append,
"Attempt to load a class outside of boot loader's module path");
// Search the shared system dictionary for classes preloaded into the
// shared spaces.
// Search for classes in the CDS archive.
InstanceKlass* k = NULL;
{
#if INCLUDE_CDS
@ -1958,7 +1957,7 @@ void SystemDictionary::initialize(TRAPS) {
// Allocate private object used as system class loader lock
_system_loader_lock_obj = oopFactory::new_intArray(0, CHECK);
// Initialize basic classes
resolve_preloaded_classes(CHECK);
resolve_well_known_classes(CHECK);
}
// Compact table of directions on the initialization of klasses:
@ -1971,6 +1970,19 @@ static const short wk_init_info[] = {
0
};
#ifdef ASSERT
bool SystemDictionary::is_well_known_klass(Symbol* class_name) {
int sid;
for (int i = 0; (sid = wk_init_info[i]) != 0; i++) {
Symbol* symbol = vmSymbols::symbol_at((vmSymbols::SID)sid);
if (class_name == symbol) {
return true;
}
}
return false;
}
#endif
bool SystemDictionary::resolve_wk_klass(WKID id, TRAPS) {
assert(id >= (int)FIRST_WKID && id < (int)WKID_LIMIT, "oob");
int sid = wk_init_info[id - FIRST_WKID];
@ -2002,8 +2014,8 @@ void SystemDictionary::resolve_wk_klasses_until(WKID limit_id, WKID &start_id, T
start_id = limit_id;
}
void SystemDictionary::resolve_preloaded_classes(TRAPS) {
assert(WK_KLASS(Object_klass) == NULL, "preloaded classes should only be initialized once");
void SystemDictionary::resolve_well_known_classes(TRAPS) {
assert(WK_KLASS(Object_klass) == NULL, "well-known classes should only be initialized once");
// Create the ModuleEntry for java.base. This call needs to be done here,
// after vmSymbols::initialize() is called but before any classes are pre-loaded.
@ -2071,7 +2083,8 @@ void SystemDictionary::resolve_preloaded_classes(TRAPS) {
WKID jsr292_group_end = WK_KLASS_ENUM_NAME(VolatileCallSite_klass);
resolve_wk_klasses_until(jsr292_group_start, scan, CHECK);
resolve_wk_klasses_through(jsr292_group_end, scan, CHECK);
resolve_wk_klasses_until(NOT_JVMCI(WKID_LIMIT) JVMCI_ONLY(FIRST_JVMCI_WKID), scan, CHECK);
WKID last = NOT_JVMCI(WKID_LIMIT) JVMCI_ONLY(FIRST_JVMCI_WKID);
resolve_wk_klasses_until(last, scan, CHECK);
_box_klasses[T_BOOLEAN] = WK_KLASS(Boolean_klass);
_box_klasses[T_CHAR] = WK_KLASS(Character_klass);
@ -2088,6 +2101,17 @@ void SystemDictionary::resolve_preloaded_classes(TRAPS) {
Method* method = InstanceKlass::cast(ClassLoader_klass())->find_method(vmSymbols::checkPackageAccess_name(), vmSymbols::class_protectiondomain_signature());
_has_checkPackageAccess = (method != NULL);
}
#ifdef ASSERT
if (UseSharedSpaces) {
assert(JvmtiExport::is_early_phase(),
"All well known classes must be resolved in JVMTI early phase");
for (int i = FIRST_WKID; i < last; i++) {
InstanceKlass* k = _well_known_klasses[i];
assert(k->is_shared(), "must not be replaced by JVMTI class file load hook");
}
}
#endif
}
// Tells if a given klass is a box (wrapper class, such as java.lang.Integer).

@ -85,19 +85,20 @@ class ProtectionDomainCacheEntry;
class GCTimer;
class OopStorage;
// Certain classes are preloaded, such as java.lang.Object and java.lang.String.
// They are all "well-known", in the sense that no class loader is allowed
// to provide a different definition.
//
// These klasses must all have names defined in vmSymbols.
#define WK_KLASS_ENUM_NAME(kname) kname##_knum
// Certain classes, such as java.lang.Object and java.lang.String,
// are "well-known", in the sense that no class loader is allowed
// to provide a different definition.
//
// Each well-known class has a short klass name (like object_klass),
// and a vmSymbol name (like java_lang_Object).
// The order of these definitions is significant; it is the order in which
// preloading is actually performed by resolve_preloaded_classes.
//
// The order of these definitions is significant: the classes are
// resolved during early VM start-up by resolve_well_known_classes
// in this order. Changing the order may require careful restructuring
// of the VM start-up sequence.
//
#define WK_KLASSES_DO(do_klass) \
/* well-known classes */ \
do_klass(Object_klass, java_lang_Object ) \
@ -127,7 +128,7 @@ class OopStorage;
do_klass(IllegalMonitorStateException_klass, java_lang_IllegalMonitorStateException ) \
do_klass(Reference_klass, java_lang_ref_Reference ) \
\
/* Preload ref klasses and set reference types */ \
/* ref klasses and set reference types */ \
do_klass(SoftReference_klass, java_lang_ref_SoftReference ) \
do_klass(WeakReference_klass, java_lang_ref_WeakReference ) \
do_klass(FinalReference_klass, java_lang_ref_FinalReference ) \
@ -200,7 +201,7 @@ class OopStorage;
/* support for stack dump lock analysis */ \
do_klass(java_util_concurrent_locks_AbstractOwnableSynchronizer_klass, java_util_concurrent_locks_AbstractOwnableSynchronizer) \
\
/* Preload boxing klasses */ \
/* boxing klasses */ \
do_klass(Boolean_klass, java_lang_Boolean ) \
do_klass(Character_klass, java_lang_Character ) \
do_klass(Float_klass, java_lang_Float ) \
@ -391,7 +392,8 @@ public:
// Initialization
static void initialize(TRAPS);
// Checked fast access to commonly used classes - mostly preloaded
// Checked fast access to the well-known classes -- so that you don't try to use them
// before they are resolved.
static InstanceKlass* check_klass(InstanceKlass* k) {
assert(k != NULL, "klass not loaded");
return k;
@ -435,6 +437,12 @@ public:
return check_klass(_box_klasses[t]);
}
static BasicType box_klass_type(Klass* k); // inverse of box_klass
#ifdef ASSERT
static bool is_well_known_klass(Klass* k) {
return is_well_known_klass(k->name());
}
static bool is_well_known_klass(Symbol* class_name);
#endif
protected:
// Returns the class loader data to be used when looking up/updating the
@ -695,8 +703,8 @@ protected:
ClassLoaderData* loader_data,
TRAPS);
// Resolve preloaded classes so they can be used like SystemDictionary::String_klass()
static void resolve_preloaded_classes(TRAPS);
// Resolve well-known classes so they can be used like SystemDictionary::String_klass()
static void resolve_well_known_classes(TRAPS);
// Class loader constraints
static void check_constraints(unsigned int hash,
@ -707,7 +715,6 @@ protected:
InstanceKlass* k, Handle loader,
TRAPS);
// Variables holding commonly used klasses (preloaded)
static InstanceKlass* _well_known_klasses[];
// table of box klasses (int_klass, etc.)

@ -914,6 +914,19 @@ void FileMapInfo::map_heap_regions_impl() {
return;
}
if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) {
ShouldNotReachHere(); // CDS should have been disabled.
// The archived objects are mapped at JVM start-up, but we don't know if
// j.l.String or j.l.Class might be replaced by the ClassFileLoadHook,
// which would make the archived String or mirror objects invalid. Let's be safe and not
// use the archived objects. These 2 classes are loaded during the JVMTI "early" stage.
//
// If JvmtiExport::has_early_class_hook_env() is false, the classes of some objects
// in the archived subgraphs may be replaced by the ClassFileLoadHook. But that's OK
// because we won't install an archived object subgraph if the klass of any of the
// referenced objects are replaced. See HeapShared::initialize_from_archived_subgraph().
}
MemRegion heap_reserved = Universe::heap()->reserved_region();
log_info(cds)("CDS archive was created with max heap size = " SIZE_FORMAT "M, and the following configuration:",
@ -1224,6 +1237,15 @@ bool FileMapInfo::_validating_shared_path_table = false;
bool FileMapInfo::initialize() {
assert(UseSharedSpaces, "UseSharedSpaces expected.");
if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) {
// CDS assumes that no classes resolved in SystemDictionary::resolve_well_known_classes
// are replaced at runtime by JVMTI ClassFileLoadHook. All of those classes are resolved
// during the JVMTI "early" stage, so we can still use CDS if
// JvmtiExport::has_early_class_hook_env() is false.
FileMapInfo::fail_continue("CDS is disabled because early JVMTI ClassFileLoadHook is in use.");
return false;
}
if (!open_for_read()) {
return false;
}

@ -417,6 +417,11 @@ void HeapShared::initialize_from_archived_subgraph(Klass* k) {
Klass* resolved_k = SystemDictionary::resolve_or_null(
(obj_k)->name(), THREAD);
if (resolved_k != obj_k) {
assert(!SystemDictionary::is_well_known_klass(resolved_k),
"shared well-known classes must not be replaced by JVMTI ClassFileLoadHook");
ResourceMark rm(THREAD);
log_info(cds, heap)("Failed to load subgraph because %s was not loaded from archive",
resolved_k->external_name());
return;
}
if ((obj_k)->is_instance_klass()) {

@ -994,6 +994,20 @@ class JvmtiClassFileLoadHookPoster : public StackObj {
}
};
bool JvmtiExport::is_early_phase() {
return JvmtiEnvBase::get_phase() <= JVMTI_PHASE_PRIMORDIAL;
}
bool JvmtiExport::has_early_class_hook_env() {
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
if (env->early_class_hook_env()) {
return true;
}
}
return false;
}
bool JvmtiExport::_should_post_class_file_load_hook = false;
// this entry is for class file load hook on class load, redefine and retransform

@ -328,6 +328,8 @@ class JvmtiExport : public AllStatic {
JVMTI_ONLY(return _should_post_class_file_load_hook);
NOT_JVMTI(return false;)
}
static bool is_early_phase();
static bool has_early_class_hook_env();
// Return true if the class was modified by the hook.
static bool post_class_file_load_hook(Symbol* h_name, Handle class_loader,
Handle h_protection_domain,

@ -0,0 +1,222 @@
/*
* Copyright (c) 2018, 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 Tests how CDS works when critical library classes are replaced with JVMTI ClassFileLoadHook
* @library /test/lib
* @requires vm.cds
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox
* @run main/othervm/native ReplaceCriticalClasses
*/
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.process.OutputAnalyzer;
import sun.hotspot.WhiteBox;
public class ReplaceCriticalClasses {
public static void main(String args[]) throws Throwable {
if (args.length == 0) {
launchChildProcesses();
} else if (args.length == 3 && args[0].equals("child")) {
Class klass = Class.forName(args[2].replace("/", "."));
if (args[1].equals("-shared")) {
testInChild(true, klass);
} else if (args[1].equals("-notshared")) {
testInChild(false, klass);
} else {
throw new RuntimeException("Unknown child exec option " + args[1]);
}
return;
} else {
throw new RuntimeException("Usage: @run main/othervm/native ReplaceCriticalClasses");
}
}
static void launchChildProcesses() throws Throwable {
String tests[] = {
// CDS should be disabled -- these critical classes will be replaced
// because JvmtiExport::early_class_hook_env() is true.
"-early -notshared java/lang/Object",
"-early -notshared java/lang/String",
"-early -notshared java/lang/Cloneable",
"-early -notshared java/io/Serializable",
// CDS should not be disabled -- these critical classes cannot be replaced because
// JvmtiExport::early_class_hook_env() is false.
"java/lang/Object",
"java/lang/String",
"java/lang/Cloneable",
"java/io/Serializable",
// Try to replace classes that are used by the archived subgraph graphs.
"-subgraph java/util/ArrayList",
"-subgraph java/lang/module/ResolvedModule",
// Replace classes that are loaded after JVMTI_PHASE_PRIMORDIAL. It's OK to replace such
// classes even when CDS is enabled. Nothing bad should happen.
"-notshared jdk/internal/vm/PostVMInitHook",
"-notshared java/util/Locale",
"-notshared sun/util/locale/BaseLocale",
"-notshared java/lang/Readable",
};
int n = 0;
for (String s : tests) {
System.out.println("Test case[" + (n++) + "] = \"" + s + "\"");
String args[] = s.split("\\s+"); // split by space character
launchChild(args);
}
}
static void launchChild(String args[]) throws Throwable {
if (args.length < 1) {
throw new RuntimeException("Invalid test case. Should be <-early> <-subgraph> <-notshared> klassName");
}
String klassName = null;
String early = "";
boolean subgraph = false;
String shared = "-shared";
for (int i=0; i<args.length-1; i++) {
String opt = args[i];
if (opt.equals("-early")) {
early = "-early,";
} else if (opt.equals("-subgraph")) {
subgraph = true;
} else if (opt.equals("-notshared")) {
shared = opt;
} else {
throw new RuntimeException("Unknown option: " + opt);
}
}
klassName = args[args.length-1];
Class.forName(klassName.replace("/", ".")); // make sure it's a valid class
// We will pass an option like "-agentlib:SimpleClassFileLoadHook=java/util/Locale,XXX,XXX".
// The SimpleClassFileLoadHook agent would attempt to hook the java/util/Locale class
// but leave the class file bytes unchanged (it replaces all bytes "XXX" with "XXX", i.e.,
// a no-op). JVMTI doesn't check the class file bytes returned by the agent, so as long
// as the agent returns a buffer, it will not load the class from CDS, and will instead
// load the class by parsing the buffer.
//
// Note that for safety we don't change the contents of the class file bytes. If in the
// future JVMTI starts checking the contents of the class file bytes, this test would need
// to be updated. (You'd see the test case with java/util/Locale staring to fail).
String agent = "-agentlib:SimpleClassFileLoadHook=" + early + klassName + ",XXX,XXX";
CDSOptions opts = (new CDSOptions())
.setXShareMode("auto")
.setUseSystemArchive(true)
.setUseVersion(false)
.addSuffix("-showversion",
"-Xlog:cds",
"-XX:+UnlockDiagnosticVMOptions",
agent,
"-XX:+WhiteBoxAPI",
"-Xbootclasspath/a:" + ClassFileInstaller.getJarPath("whitebox.jar"));
if (subgraph) {
opts.addSuffix("-Xlog:cds+heap",
"-Xlog:class+load");
}
opts.addSuffix("ReplaceCriticalClasses",
"child",
shared,
klassName);
final boolean expectDisable = !early.equals("");
final boolean checkSubgraph = subgraph;
CDSTestUtils.run(opts).assertNormalExit(out -> {
if (expectDisable) {
out.shouldContain("UseSharedSpaces: CDS is disabled because early JVMTI ClassFileLoadHook is in use.");
System.out.println("CDS disabled as expected");
}
if (checkSubgraph) {
// As of 2018/10/21 the classes in the archived subgraphs won't be
// replaced because all archived subgraphs were loaded in JVMTI_PHASE_PRIMORDIAL.
//
// This is the first class to be loaded after JVMTI has exited JVMTI_PHASE_PRIMORDIAL.
// Make sure no subgraphs are loaded afterwards.
//
// Can't use out.shouldNotMatch() because that doesn't match across multiple lines.
String firstNonPrimordialClass = "jdk.jfr.internal.EventWriter";
String regexp = firstNonPrimordialClass + ".*initialize_from_archived_subgraph";
Pattern regex = Pattern.compile(regexp, Pattern.DOTALL);
Matcher matcher = regex.matcher(out.getStdout());
if (matcher.find()) {
out.reportDiagnosticSummary();
throw new RuntimeException("'" + regexp
+ "' found in stdout: '" + matcher.group() + "' \n");
}
}
});
}
static void testInChild(boolean shouldBeShared, Class klass) {
WhiteBox wb = WhiteBox.getWhiteBox();
if (shouldBeShared && !wb.isSharedClass(klass)) {
throw new RuntimeException(klass + " should be shared but but actually is not.");
}
if (!shouldBeShared && wb.isSharedClass(klass)) {
throw new RuntimeException(klass + " should not be shared but actually is.");
}
System.out.println("wb.isSharedClass(klass): " + wb.isSharedClass(klass) + " == " + shouldBeShared);
String strings[] = {
// interned strings from j.l.Object
"@",
"nanosecond timeout value out of range",
"timeoutMillis value is negative",
// interned strings from j.l.Integer
"0",
"0X",
"0x",
"int"
};
// Make sure the interned string table is same
for (String s : strings) {
String i = s.intern();
if (s != i) {
throw new RuntimeException("Interned string mismatch: \"" + s + "\" @ " + System.identityHashCode(s) +
" vs \"" + i + "\" @ " + System.identityHashCode(i));
}
}
// We have tried to use ClassFileLoadHook to replace critical library classes (which may
// may not have succeeded, depending on whether the agent has requested
// can_generate_all_class_hook_events/can_generate_early_class_hook_events capabilities).
//
// In any case, the JVM should have started properly (perhaps with CDS disabled) and
// the above operations should succeed.
System.out.println("If I can come to here without crashing, things should be OK");
}
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2018, 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
@ -91,17 +91,23 @@ ClassFileLoadHook(jvmtiEnv *jvmti_env, JNIEnv *env, jclass class_beeing_redefine
*new_class_data_len = class_data_len;
*new_class_data = new_data;
fprintf(stderr, "Rewriting done. Replaced %d occurrence(s)\n", count);
fprintf(stderr, "Rewriting done. Replaced %d occurrence(s) of \"%s\" to \"%s\"\n", count, FROM, TO);
}
}
}
static int early = 0;
static jint init_options(char *options) {
char* class_name;
char* from;
char* to;
fprintf(stderr, "Agent library loaded with options = %s\n", options);
if (options != NULL && strncmp(options, "-early,", 7) == 0) {
early = 1;
options += 7;
}
if ((class_name = options) != NULL &&
(from = strchr(class_name, ',')) != NULL && (from[1] != 0)) {
*from = 0;
@ -132,6 +138,7 @@ static jint init_options(char *options) {
static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
int rc;
jvmtiCapabilities caps;
if ((rc = (*jvm)->GetEnv(jvm, (void **)&jvmti, JVMTI_VERSION_1_1)) != JNI_OK) {
fprintf(stderr, "Unable to create jvmtiEnv, GetEnv failed, error = %d\n", rc);
@ -141,6 +148,19 @@ static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
return JNI_ERR;
}
memset(&caps, 0, sizeof(caps));
caps.can_redefine_classes = 1;
if (early) {
fprintf(stderr, "can_generate_all_class_hook_events/can_generate_early_vmstart/can_generate_early_class_hook_events == 1\n");
caps.can_generate_all_class_hook_events = 1;
caps.can_generate_early_class_hook_events = 1;
}
if ((rc = (*jvmti)->AddCapabilities(jvmti, &caps)) != JNI_OK) {
fprintf(stderr, "AddCapabilities failed, error = %d\n", rc);
return JNI_ERR;
}
(void) memset(&callbacks, 0, sizeof(callbacks));
callbacks.ClassFileLoadHook = &ClassFileLoadHook;
if ((rc = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks))) != JNI_OK) {

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -31,6 +31,7 @@ public class CDSOptions {
public String archiveName;
public ArrayList<String> prefix = new ArrayList<String>();
public ArrayList<String> suffix = new ArrayList<String>();
public boolean useSystemArchive = false;
// Indicate whether to append "-version" when using CDS Archive.
// Most of tests will use '-version'
@ -68,4 +69,9 @@ public class CDSOptions {
this.useVersion = use;
return this;
}
public CDSOptions setUseSystemArchive(boolean use) {
this.useSystemArchive = use;
return this;
}
}

@ -82,21 +82,21 @@ public class CDSTestUtils {
*
* Instead, the test case should be written as
*
* CCDSTestUtils.run(args).assertNormalExit("Hi");
* CDSTestUtils.run(args).assertNormalExit("Hi");
*
* EXAMPLES/HOWTO
*
* 1. For simple substring matching:
*
* CCDSTestUtils.run(args).assertNormalExit("Hi");
* CCDSTestUtils.run(args).assertNormalExit("a", "b", "x");
* CCDSTestUtils.run(args).assertAbnormalExit("failure 1", "failure2");
* CDSTestUtils.run(args).assertNormalExit("Hi");
* CDSTestUtils.run(args).assertNormalExit("a", "b", "x");
* CDSTestUtils.run(args).assertAbnormalExit("failure 1", "failure2");
*
* 2. For more complex output matching: using Lambda expressions
*
* CCDSTestUtils.run(args)
* CDSTestUtils.run(args)
* .assertNormalExit(output -> output.shouldNotContain("this should not be printed");
* CCDSTestUtils.run(args)
* CDSTestUtils.run(args)
* .assertAbnormalExit(output -> {
* output.shouldNotContain("this should not be printed");
* output.shouldHaveExitValue(123);
@ -104,13 +104,13 @@ public class CDSTestUtils {
*
* 3. Chaining several checks:
*
* CCDSTestUtils.run(args)
* CDSTestUtils.run(args)
* .assertNormalExit(output -> output.shouldNotContain("this should not be printed")
* .assertNormalExit("should have this", "should have that");
*
* 4. [Rare use case] if a test sometimes exit normally, and sometimes abnormally:
*
* CCDSTestUtils.run(args)
* CDSTestUtils.run(args)
* .ifNormalExit("ths string is printed when exiting with 0")
* .ifAbNormalExit("ths string is printed when exiting with 1");
*
@ -388,9 +388,11 @@ public class CDSTestUtils {
cmd.add("-Xshare:" + opts.xShareMode);
cmd.add("-Dtest.timeout.factor=" + TestTimeoutFactor);
if (opts.archiveName == null)
opts.archiveName = getDefaultArchiveName();
cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
if (!opts.useSystemArchive) {
if (opts.archiveName == null)
opts.archiveName = getDefaultArchiveName();
cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
}
if (opts.useVersion)
cmd.add("-version");