--- a/hotspot/src/os/windows/vm/os_windows.cpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/os/windows/vm/os_windows.cpp Tue Mar 11 15:14:46 2014 +0100
@@ -3619,13 +3619,14 @@
"possibility of dangling Thread pointer");
OSThread* osthread = thread->osthread();
- bool interrupted = osthread->interrupted();
// There is no synchronization between the setting of the interrupt
// and it being cleared here. It is critical - see 6535709 - that
// we only clear the interrupt state, and reset the interrupt event,
// if we are going to report that we were indeed interrupted - else
// an interrupt can be "lost", leading to spurious wakeups or lost wakeups
- // depending on the timing
+ // depending on the timing. By checking thread interrupt event to see
+ // if the thread gets real interrupt thus prevent spurious wakeup.
+ bool interrupted = osthread->interrupted() && (WaitForSingleObject(osthread->interrupt_event(), 0) == WAIT_OBJECT_0);
if (interrupted && clear_interrupted) {
osthread->set_interrupted(false);
ResetEvent(osthread->interrupt_event());
--- a/hotspot/src/share/vm/code/debugInfo.hpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/code/debugInfo.hpp Tue Mar 11 15:14:46 2014 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, 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
@@ -273,8 +273,8 @@
}
Method* read_method() {
Method* o = (Method*)(code()->metadata_at(read_int()));
- assert(o == NULL ||
- o->is_metaspace_object(), "meta data only");
+ // is_metadata() is a faster check than is_metaspace_object()
+ assert(o == NULL || o->is_metadata(), "meta data only");
return o;
}
ScopeValue* read_object_value();
--- a/hotspot/src/share/vm/oops/constantPool.cpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/oops/constantPool.cpp Tue Mar 11 15:14:46 2014 +0100
@@ -1295,6 +1295,7 @@
} break;
case JVM_CONSTANT_UnresolvedClass:
+ case JVM_CONSTANT_UnresolvedClassInError:
{
// Can be resolved after checking tag, so check the slot first.
CPSlot entry = from_cp->slot_at(from_i);
--- a/hotspot/src/share/vm/oops/metadata.hpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/oops/metadata.hpp Tue Mar 11 15:14:46 2014 +0100
@@ -42,6 +42,7 @@
// Rehashing support for tables containing pointers to this
unsigned int new_hash(juint seed) { ShouldNotReachHere(); return 0; }
+ virtual bool is_metadata() const volatile { return true; }
virtual bool is_klass() const volatile { return false; }
virtual bool is_method() const volatile { return false; }
virtual bool is_methodData() const volatile { return false; }
--- a/hotspot/src/share/vm/opto/library_call.cpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/opto/library_call.cpp Tue Mar 11 15:14:46 2014 +0100
@@ -3180,7 +3180,8 @@
// private native boolean java.lang.Thread.isInterrupted(boolean ClearInterrupted);
bool LibraryCallKit::inline_native_isInterrupted() {
// Add a fast path to t.isInterrupted(clear_int):
- // (t == Thread.current() && (!TLS._osthread._interrupted || !clear_int))
+ // (t == Thread.current() &&
+ // (!TLS._osthread._interrupted || WINDOWS_ONLY(false) NOT_WINDOWS(!clear_int)))
// ? TLS._osthread._interrupted : /*slow path:*/ t.isInterrupted(clear_int)
// So, in the common case that the interrupt bit is false,
// we avoid making a call into the VM. Even if the interrupt bit
@@ -3237,6 +3238,7 @@
// drop through to next case
set_control( _gvn.transform(new (C) IfTrueNode(iff_bit)));
+#ifndef TARGET_OS_FAMILY_windows
// (c) Or, if interrupt bit is set and clear_int is false, use 2nd fast path.
Node* clr_arg = argument(1);
Node* cmp_arg = _gvn.transform(new (C) CmpINode(clr_arg, intcon(0)));
@@ -3250,6 +3252,10 @@
// drop through to next case
set_control( _gvn.transform(new (C) IfTrueNode(iff_arg)));
+#else
+ // To return true on Windows you must read the _interrupted field
+ // and check the the event state i.e. take the slow path.
+#endif // TARGET_OS_FAMILY_windows
// (d) Otherwise, go to the slow path.
slow_region->add_req(control());
--- a/hotspot/src/share/vm/prims/jvm.cpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/prims/jvm.cpp Tue Mar 11 15:14:46 2014 +0100
@@ -4360,7 +4360,7 @@
JVM_ENTRY(void, JVM_GetVersionInfo(JNIEnv* env, jvm_version_info* info, size_t info_size))
{
- memset(info, 0, sizeof(info_size));
+ memset(info, 0, info_size);
info->jvm_version = Abstract_VM_Version::jvm_version();
info->update_version = 0; /* 0 in HotSpot Express VM */
--- a/hotspot/src/share/vm/prims/jvmtiEnv.cpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiEnv.cpp Tue Mar 11 15:14:46 2014 +0100
@@ -1464,7 +1464,19 @@
// It's fine to update the thread state here because no JVMTI events
// shall be posted for this PopFrame.
- state->update_for_pop_top_frame();
+ // It is only safe to perform the direct operation on the current
+ // thread. All other usage needs to use a vm-safepoint-op for safety.
+ if (java_thread == JavaThread::current()) {
+ state->update_for_pop_top_frame();
+ } else {
+ VM_UpdateForPopTopFrame op(state);
+ VMThread::execute(&op);
+ jvmtiError err = op.result();
+ if (err != JVMTI_ERROR_NONE) {
+ return err;
+ }
+ }
+
java_thread->set_popframe_condition(JavaThread::popframe_pending_bit);
// Set pending step flag for this popframe and it is cleared when next
// step event is posted.
@@ -1505,6 +1517,7 @@
// depth - pre-checked as non-negative
jvmtiError
JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) {
+ jvmtiError err = JVMTI_ERROR_NONE;
ResourceMark rm;
uint32_t debug_bits = 0;
@@ -1532,10 +1545,17 @@
assert(vf->frame_pointer() != NULL, "frame pointer mustn't be NULL");
- int frame_number = state->count_frames() - depth;
- state->env_thread_state(this)->set_frame_pop(frame_number);
-
- return JVMTI_ERROR_NONE;
+ // It is only safe to perform the direct operation on the current
+ // thread. All other usage needs to use a vm-safepoint-op for safety.
+ if (java_thread == JavaThread::current()) {
+ int frame_number = state->count_frames() - depth;
+ state->env_thread_state(this)->set_frame_pop(frame_number);
+ } else {
+ VM_SetFramePop op(this, state, depth);
+ VMThread::execute(&op);
+ err = op.result();
+ }
+ return err;
} /* end NotifyFramePop */
--- a/hotspot/src/share/vm/prims/jvmtiEnvBase.hpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiEnvBase.hpp Tue Mar 11 15:14:46 2014 +0100
@@ -29,6 +29,7 @@
#include "prims/jvmtiEnvThreadState.hpp"
#include "prims/jvmtiEventController.hpp"
#include "prims/jvmtiThreadState.hpp"
+#include "prims/jvmtiThreadState.inline.hpp"
#include "runtime/fieldDescriptor.hpp"
#include "runtime/frame.hpp"
#include "runtime/handles.inline.hpp"
@@ -334,6 +335,60 @@
JvmtiEnv* next(JvmtiEnvBase* env) { return env->next_environment(); }
};
+// VM operation to update for pop top frame.
+class VM_UpdateForPopTopFrame : public VM_Operation {
+private:
+ JvmtiThreadState* _state;
+ jvmtiError _result;
+
+public:
+ VM_UpdateForPopTopFrame(JvmtiThreadState* state) {
+ _state = state;
+ _result = JVMTI_ERROR_NONE;
+ }
+ VMOp_Type type() const { return VMOp_UpdateForPopTopFrame; }
+ jvmtiError result() { return _result; }
+ void doit() {
+ JavaThread* jt = _state->get_thread();
+ if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
+ _state->update_for_pop_top_frame();
+ } else {
+ _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ }
+ }
+};
+
+// VM operation to set frame pop.
+class VM_SetFramePop : public VM_Operation {
+private:
+ JvmtiEnv *_env;
+ JvmtiThreadState* _state;
+ jint _depth;
+ jvmtiError _result;
+
+public:
+ VM_SetFramePop(JvmtiEnv *env, JvmtiThreadState* state, jint depth) {
+ _env = env;
+ _state = state;
+ _depth = depth;
+ _result = JVMTI_ERROR_NONE;
+ }
+ // Nested operation must be allowed for the VM_EnterInterpOnlyMode that is
+ // called from the JvmtiEventControllerPrivate::recompute_thread_enabled.
+ bool allow_nested_vm_operations() const { return true; }
+ VMOp_Type type() const { return VMOp_SetFramePop; }
+ jvmtiError result() { return _result; }
+ void doit() {
+ JavaThread* jt = _state->get_thread();
+ if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
+ int frame_number = _state->count_frames() - _depth;
+ _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number);
+ } else {
+ _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ }
+ }
+};
+
// VM operation to get monitor information with stack depth.
class VM_GetOwnedMonitorInfo : public VM_Operation {
--- a/hotspot/src/share/vm/prims/jvmtiEnvThreadState.cpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiEnvThreadState.cpp Tue Mar 11 15:14:46 2014 +0100
@@ -190,12 +190,8 @@
JvmtiFramePops* JvmtiEnvThreadState::get_frame_pops() {
-#ifdef ASSERT
- uint32_t debug_bits = 0;
-#endif
- assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
- "frame pop data only accessible from same thread or while suspended");
-
+ assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(),
+ "frame pop data only accessible from same thread or at safepoint");
if (_frame_pops == NULL) {
_frame_pops = new JvmtiFramePops();
assert(_frame_pops != NULL, "_frame_pops != NULL");
@@ -209,44 +205,32 @@
}
void JvmtiEnvThreadState::set_frame_pop(int frame_number) {
-#ifdef ASSERT
- uint32_t debug_bits = 0;
-#endif
- assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
- "frame pop data only accessible from same thread or while suspended");
+ assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(),
+ "frame pop data only accessible from same thread or at safepoint");
JvmtiFramePop fpop(frame_number);
JvmtiEventController::set_frame_pop(this, fpop);
}
void JvmtiEnvThreadState::clear_frame_pop(int frame_number) {
-#ifdef ASSERT
- uint32_t debug_bits = 0;
-#endif
- assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
- "frame pop data only accessible from same thread or while suspended");
+ assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(),
+ "frame pop data only accessible from same thread or at safepoint");
JvmtiFramePop fpop(frame_number);
JvmtiEventController::clear_frame_pop(this, fpop);
}
void JvmtiEnvThreadState::clear_to_frame_pop(int frame_number) {
-#ifdef ASSERT
- uint32_t debug_bits = 0;
-#endif
- assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
- "frame pop data only accessible from same thread or while suspended");
+ assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(),
+ "frame pop data only accessible from same thread or at safepoint");
JvmtiFramePop fpop(frame_number);
JvmtiEventController::clear_to_frame_pop(this, fpop);
}
bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) {
-#ifdef ASSERT
- uint32_t debug_bits = 0;
-#endif
- assert(get_thread() == Thread::current() || JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
- "frame pop data only accessible from same thread or while suspended");
+ assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(),
+ "frame pop data only accessible from same thread or at safepoint");
if (!get_thread()->is_interp_only_mode() || _frame_pops == NULL) {
return false;
}
--- a/hotspot/src/share/vm/prims/jvmtiEventController.cpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiEventController.cpp Tue Mar 11 15:14:46 2014 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, 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
@@ -989,21 +989,21 @@
void
JvmtiEventController::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
- MutexLocker mu(JvmtiThreadState_lock);
+ MutexLockerEx mu(SafepointSynchronize::is_at_safepoint() ? NULL : JvmtiThreadState_lock);
JvmtiEventControllerPrivate::set_frame_pop(ets, fpop);
}
void
JvmtiEventController::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
- MutexLocker mu(JvmtiThreadState_lock);
+ MutexLockerEx mu(SafepointSynchronize::is_at_safepoint() ? NULL : JvmtiThreadState_lock);
JvmtiEventControllerPrivate::clear_frame_pop(ets, fpop);
}
void
JvmtiEventController::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
- MutexLocker mu(JvmtiThreadState_lock);
+ MutexLockerEx mu(SafepointSynchronize::is_at_safepoint() ? NULL : JvmtiThreadState_lock);
JvmtiEventControllerPrivate::clear_to_frame_pop(ets, fpop);
}
--- a/hotspot/src/share/vm/prims/jvmtiThreadState.cpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiThreadState.cpp Tue Mar 11 15:14:46 2014 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, 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
@@ -63,6 +63,7 @@
_vm_object_alloc_event_collector = NULL;
_the_class_for_redefinition_verification = NULL;
_scratch_class_for_redefinition_verification = NULL;
+ _cur_stack_depth = UNKNOWN_STACK_DEPTH;
// JVMTI ForceEarlyReturn support
_pending_step_for_earlyret = false;
@@ -213,12 +214,9 @@
// Helper routine used in several places
int JvmtiThreadState::count_frames() {
-#ifdef ASSERT
- uint32_t debug_bits = 0;
-#endif
- assert(SafepointSynchronize::is_at_safepoint() ||
- JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
- "at safepoint or must be suspended");
+ guarantee(SafepointSynchronize::is_at_safepoint() ||
+ (JavaThread *)Thread::current() == get_thread(),
+ "must be current thread or at safepoint");
if (!get_thread()->has_last_Java_frame()) return 0; // no Java frames
@@ -243,15 +241,9 @@
void JvmtiThreadState::invalidate_cur_stack_depth() {
- Thread *cur = Thread::current();
- uint32_t debug_bits = 0;
-
- // The caller can be the VMThread at a safepoint, the current thread
- // or the target thread must be suspended.
- guarantee((cur->is_VM_thread() && SafepointSynchronize::is_at_safepoint()) ||
- (JavaThread *)cur == get_thread() ||
- JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
- "sanity check");
+ guarantee(SafepointSynchronize::is_at_safepoint() ||
+ (JavaThread *)Thread::current() == get_thread(),
+ "must be current thread or at safepoint");
_cur_stack_depth = UNKNOWN_STACK_DEPTH;
}
@@ -280,10 +272,9 @@
}
int JvmtiThreadState::cur_stack_depth() {
- uint32_t debug_bits = 0;
- guarantee(JavaThread::current() == get_thread() ||
- JvmtiEnv::is_thread_fully_suspended(get_thread(), false, &debug_bits),
- "must be current thread or suspended");
+ guarantee(SafepointSynchronize::is_at_safepoint() ||
+ (JavaThread *)Thread::current() == get_thread(),
+ "must be current thread or at safepoint");
if (!is_interp_only_mode() || _cur_stack_depth == UNKNOWN_STACK_DEPTH) {
_cur_stack_depth = count_frames();
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp Tue Mar 11 15:14:46 2014 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, 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
@@ -74,6 +74,8 @@
template(PopulateDumpSharedSpace) \
template(JNIFunctionTableCopier) \
template(RedefineClasses) \
+ template(UpdateForPopTopFrame) \
+ template(SetFramePop) \
template(GetOwnedMonitorInfo) \
template(GetObjectMonitorUsage) \
template(GetCurrentContendedMonitor) \
--- a/hotspot/test/runtime/6294277/SourceDebugExtension.java Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/test/runtime/6294277/SourceDebugExtension.java Tue Mar 11 15:14:46 2014 +0100
@@ -25,7 +25,7 @@
* @test
* @bug 6294277
* @summary java -Xdebug crashes on SourceDebugExtension attribute larger than 64K
- * @run main/othervm -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=n SourceDebugExtension
+ * @run main/othervm -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n SourceDebugExtension
*/
import java.io.*;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/jvmti/TestRedefineWithUnresolvedClass.java Tue Mar 11 15:14:46 2014 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014, 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 Redefine a class with an UnresolvedClass reference in the constant pool.
+ * @bug 8035150
+ * @library /testlibrary
+ * @build UnresolvedClassAgent com.oracle.java.testlibrary.ProcessTools com.oracle.java.testlibrary.OutputAnalyzer
+ * @run main TestRedefineWithUnresolvedClass
+ */
+
+import java.io.File;
+import java.util.Arrays;
+
+import com.oracle.java.testlibrary.OutputAnalyzer;
+import com.oracle.java.testlibrary.ProcessTools;
+
+public class TestRedefineWithUnresolvedClass {
+
+ final static String slash = File.separator;
+ final static String testClasses = System.getProperty("test.classes") + slash;
+
+ public static void main(String... args) throws Throwable {
+ // delete this class to cause a NoClassDefFoundError
+ File unresolved = new File(testClasses, "MyUnresolvedClass.class");
+ if (unresolved.exists() && !unresolved.delete()) {
+ throw new Exception("Could not delete: " + unresolved);
+ }
+
+ // build the javaagent
+ buildJar("UnresolvedClassAgent");
+
+ // launch a VM with the javaagent
+ launchTest();
+ }
+
+ private static void buildJar(String jarName) throws Throwable {
+ String testSrc = System.getProperty("test.src", "?") + slash;
+
+ String jarPath = String.format("%s%s.jar", testClasses, jarName);
+ String manifestPath = String.format("%s%s.mf", testSrc, jarName);
+ String className = String.format("%s.class", jarName);
+
+ String[] args = new String[] {"-cfm", jarPath, manifestPath, "-C", testClasses, className};
+
+ System.out.println("Running jar " + Arrays.toString(args));
+ sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar");
+ if (!jarTool.run(args)) {
+ throw new Exception("jar failed: args=" + Arrays.toString(args));
+ }
+ }
+
+ private static void launchTest() throws Throwable {
+ String[] args = {
+ "-javaagent:" + testClasses + "UnresolvedClassAgent.jar",
+ "-Dtest.classes=" + testClasses,
+ "UnresolvedClassAgent" };
+ OutputAnalyzer output = ProcessTools.executeTestJvm(args);
+ output.shouldHaveExitValue(0);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/jvmti/UnresolvedClassAgent.java Tue Mar 11 15:14:46 2014 +0100
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014, 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.File;
+import java.io.FileInputStream;
+import java.lang.instrument.ClassDefinition;
+import java.lang.instrument.Instrumentation;
+
+/*
+ * This class is present during compilation, but will be deleted before execution.
+ */
+class MyUnresolvedClass {
+ static void bar() {
+ }
+}
+
+class MyRedefinedClass {
+ static void foo() {
+ MyUnresolvedClass.bar();
+ }
+}
+
+public class UnresolvedClassAgent {
+ public static void main(String... args) {
+ }
+
+ public static void premain(String args, Instrumentation inst) throws Exception {
+ try {
+ MyRedefinedClass.foo();
+ } catch(NoClassDefFoundError err) {
+ System.out.println("NoClassDefFoundError (expected)");
+ }
+
+ File f = new File(System.getProperty("test.classes"), "MyRedefinedClass.class");
+ byte[] buf = new byte[(int)f.length()];
+ try (DataInputStream dis = new DataInputStream(new FileInputStream(f))) {
+ dis.readFully(buf);
+ }
+ ClassDefinition cd = new ClassDefinition(MyRedefinedClass.class, buf);
+ inst.redefineClasses(new ClassDefinition[] {cd});
+
+ try {
+ MyRedefinedClass.foo();
+ } catch(NoClassDefFoundError err) {
+ System.out.println("NoClassDefFoundError (expected again)");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/jvmti/UnresolvedClassAgent.mf Tue Mar 11 15:14:46 2014 +0100
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Premain-Class: UnresolvedClassAgent
+Can-Redefine-Classes: true
--- a/hotspot/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java Fri Feb 21 09:48:52 2014 +0100
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java Tue Mar 11 15:14:46 2014 +0100
@@ -163,10 +163,87 @@
// Reporting
StringBuilder cmdLine = new StringBuilder();
- for (String cmd : args)
- cmdLine.append(cmd).append(' ');
+ for (String cmd : args) {
+ cmdLine.append(cmd).append(' ');
+ }
System.out.println("Command line: [" + cmdLine.toString() + "]");
return new ProcessBuilder(args.toArray(new String[args.size()]));
}
+
+ /**
+ * Executes a test jvm process, waits for it to finish and returns the process output.
+ * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added.
+ * The java from the test.jdk is used to execute the command.
+ *
+ * The command line will be like:
+ * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds
+ *
+ * @param cmds User specifed arguments.
+ * @return The output from the process.
+ */
+ public static OutputAnalyzer executeTestJvm(String... cmds) throws Throwable {
+ ProcessBuilder pb = createJavaProcessBuilder(Utils.addTestJavaOpts(cmds));
+ return executeProcess(pb);
+ }
+
+ /**
+ * Executes a process, waits for it to finish and returns the process output.
+ * @param pb The ProcessBuilder to execute.
+ * @return The output from the process.
+ */
+ public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Throwable {
+ OutputAnalyzer output = null;
+ try {
+ output = new OutputAnalyzer(pb.start());
+ return output;
+ } catch (Throwable t) {
+ System.out.println("executeProcess() failed: " + t);
+ throw t;
+ } finally {
+ System.out.println(getProcessLog(pb, output));
+ }
+ }
+
+ /**
+ * Executes a process, waits for it to finish and returns the process output.
+ * @param cmds The command line to execute.
+ * @return The output from the process.
+ */
+ public static OutputAnalyzer executeProcess(String... cmds) throws Throwable {
+ return executeProcess(new ProcessBuilder(cmds));
+ }
+
+ /**
+ * Used to log command line, stdout, stderr and exit code from an executed process.
+ * @param pb The executed process.
+ * @param output The output from the process.
+ */
+ public static String getProcessLog(ProcessBuilder pb, OutputAnalyzer output) {
+ String stderr = output == null ? "null" : output.getStderr();
+ String stdout = output == null ? "null" : output.getStdout();
+ String exitValue = output == null ? "null": Integer.toString(output.getExitValue());
+ StringBuilder logMsg = new StringBuilder();
+ final String nl = System.getProperty("line.separator");
+ logMsg.append("--- ProcessLog ---" + nl);
+ logMsg.append("cmd: " + getCommandLine(pb) + nl);
+ logMsg.append("exitvalue: " + exitValue + nl);
+ logMsg.append("stderr: " + stderr + nl);
+ logMsg.append("stdout: " + stdout + nl);
+ return logMsg.toString();
+ }
+
+ /**
+ * @return The full command line for the ProcessBuilder.
+ */
+ public static String getCommandLine(ProcessBuilder pb) {
+ if (pb == null) {
+ return "null";
+ }
+ StringBuilder cmd = new StringBuilder();
+ for (String s : pb.command()) {
+ cmd.append(s).append(" ");
+ }
+ return cmd.toString().trim();
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/Utils.java Tue Mar 11 15:14:46 2014 +0100
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+import static com.oracle.java.testlibrary.Asserts.assertTrue;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * Common library for various test helper functions.
+ */
+public final class Utils {
+
+ /**
+ * Returns the sequence used by operating system to separate lines.
+ */
+ public static final String NEW_LINE = System.getProperty("line.separator");
+
+ /**
+ * Returns the value of 'test.vm.opts'system property.
+ */
+ public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim();
+
+ /**
+ * Returns the value of 'test.java.opts'system property.
+ */
+ public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim();
+
+ /**
+ * Returns the value of 'test.timeout.factor' system property
+ * converted to {@code double}.
+ */
+ public static final double TIMEOUT_FACTOR;
+ static {
+ String toFactor = System.getProperty("test.timeout.factor", "1.0");
+ TIMEOUT_FACTOR = Double.parseDouble(toFactor);
+ }
+
+ private Utils() {
+ // Private constructor to prevent class instantiation
+ }
+
+ /**
+ * Returns the list of VM options.
+ *
+ * @return List of VM options
+ */
+ public static List<String> getVmOptions() {
+ return Arrays.asList(safeSplitString(VM_OPTIONS));
+ }
+
+ /**
+ * Returns the list of VM options with -J prefix.
+ *
+ * @return The list of VM options with -J prefix
+ */
+ public static List<String> getForwardVmOptions() {
+ String[] opts = safeSplitString(VM_OPTIONS);
+ for (int i = 0; i < opts.length; i++) {
+ opts[i] = "-J" + opts[i];
+ }
+ return Arrays.asList(opts);
+ }
+
+ /**
+ * Returns the default JTReg arguments for a jvm running a test.
+ * This is the combination of JTReg arguments test.vm.opts and test.java.opts.
+ * @return An array of options, or an empty array if no opptions.
+ */
+ public static String[] getTestJavaOpts() {
+ List<String> opts = new ArrayList<String>();
+ Collections.addAll(opts, safeSplitString(VM_OPTIONS));
+ Collections.addAll(opts, safeSplitString(JAVA_OPTIONS));
+ return opts.toArray(new String[0]);
+ }
+
+ /**
+ * Combines given arguments with default JTReg arguments for a jvm running a test.
+ * This is the combination of JTReg arguments test.vm.opts and test.java.opts
+ * @return The combination of JTReg test java options and user args.
+ */
+ public static String[] addTestJavaOpts(String... userArgs) {
+ List<String> opts = new ArrayList<String>();
+ Collections.addAll(opts, getTestJavaOpts());
+ Collections.addAll(opts, userArgs);
+ return opts.toArray(new String[0]);
+ }
+
+ /**
+ * Splits a string by white space.
+ * Works like String.split(), but returns an empty array
+ * if the string is null or empty.
+ */
+ private static String[] safeSplitString(String s) {
+ if (s == null || s.trim().isEmpty()) {
+ return new String[] {};
+ }
+ return s.trim().split("\\s+");
+ }
+
+ /**
+ * @return The full command line for the ProcessBuilder.
+ */
+ public static String getCommandLine(ProcessBuilder pb) {
+ StringBuilder cmd = new StringBuilder();
+ for (String s : pb.command()) {
+ cmd.append(s).append(" ");
+ }
+ return cmd.toString();
+ }
+
+ /**
+ * Returns the free port on the local host.
+ * The function will spin until a valid port number is found.
+ *
+ * @return The port number
+ * @throws InterruptedException if any thread has interrupted the current thread
+ * @throws IOException if an I/O error occurs when opening the socket
+ */
+ public static int getFreePort() throws InterruptedException, IOException {
+ int port = -1;
+
+ while (port <= 0) {
+ Thread.sleep(100);
+
+ ServerSocket serverSocket = null;
+ try {
+ serverSocket = new ServerSocket(0);
+ port = serverSocket.getLocalPort();
+ } finally {
+ serverSocket.close();
+ }
+ }
+
+ return port;
+ }
+
+ /**
+ * Returns the name of the local host.
+ *
+ * @return The host name
+ * @throws UnknownHostException if IP address of a host could not be determined
+ */
+ public static String getHostname() throws UnknownHostException {
+ InetAddress inetAddress = InetAddress.getLocalHost();
+ String hostName = inetAddress.getHostName();
+
+ assertTrue((hostName != null && !hostName.isEmpty()),
+ "Cannot get hostname");
+
+ return hostName;
+ }
+
+ /**
+ * Uses "jcmd -l" to search for a jvm pid. This function will wait
+ * forever (until jtreg timeout) for the pid to be found.
+ * @param key Regular expression to search for
+ * @return The found pid.
+ */
+ public static int waitForJvmPid(String key) throws Throwable {
+ final long iterationSleepMillis = 250;
+ System.out.println("waitForJvmPid: Waiting for key '" + key + "'");
+ System.out.flush();
+ while (true) {
+ int pid = tryFindJvmPid(key);
+ if (pid >= 0) {
+ return pid;
+ }
+ Thread.sleep(iterationSleepMillis);
+ }
+ }
+
+ /**
+ * Searches for a jvm pid in the output from "jcmd -l".
+ *
+ * Example output from jcmd is:
+ * 12498 sun.tools.jcmd.JCmd -l
+ * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar
+ *
+ * @param key A regular expression to search for.
+ * @return The found pid, or -1 if Enot found.
+ * @throws Exception If multiple matching jvms are found.
+ */
+ public static int tryFindJvmPid(String key) throws Throwable {
+ OutputAnalyzer output = null;
+ try {
+ JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd");
+ jcmdLauncher.addToolArg("-l");
+ output = ProcessTools.executeProcess(jcmdLauncher.getCommand());
+ output.shouldHaveExitValue(0);
+
+ // Search for a line starting with numbers (pid), follwed by the key.
+ Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n");
+ Matcher matcher = pattern.matcher(output.getStdout());
+
+ int pid = -1;
+ if (matcher.find()) {
+ pid = Integer.parseInt(matcher.group(1));
+ System.out.println("findJvmPid.pid: " + pid);
+ if (matcher.find()) {
+ throw new Exception("Found multiple JVM pids for key: " + key);
+ }
+ }
+ return pid;
+ } catch (Throwable t) {
+ System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t));
+ throw t;
+ }
+ }
+
+ /**
+ * Returns file content as a list of strings
+ *
+ * @param file File to operate on
+ * @return List of strings
+ * @throws IOException
+ */
+ public static List<String> fileAsList(File file) throws IOException {
+ assertTrue(file.exists() && file.isFile(),
+ file.getAbsolutePath() + " does not exist or not a file");
+ List<String> output = new ArrayList<>();
+ try (BufferedReader reader = new BufferedReader(new FileReader(file.getAbsolutePath()))) {
+ while (reader.ready()) {
+ output.add(reader.readLine().replace(NEW_LINE, ""));
+ }
+ }
+ return output;
+ }
+
+}