# HG changeset patch # User amurillo # Date 1394581775 25200 # Node ID 74dd0c7b2de1307cbbea061ce953c34a3585a320 # Parent 3812c088b9456ee22c933e88aee1ece71f4e783a# Parent 7380034e5b31bbc66f88d56df524706a531dd8de Merge diff -r 3812c088b945 -r 74dd0c7b2de1 src/os/windows/vm/os_windows.cpp --- a/src/os/windows/vm/os_windows.cpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/os/windows/vm/os_windows.cpp Tue Mar 11 16:49:35 2014 -0700 @@ -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()); diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/code/debugInfo.hpp --- a/src/share/vm/code/debugInfo.hpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/code/debugInfo.hpp Tue Mar 11 16:49:35 2014 -0700 @@ -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(); diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/oops/constantPool.cpp --- a/src/share/vm/oops/constantPool.cpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/oops/constantPool.cpp Tue Mar 11 16:49:35 2014 -0700 @@ -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); diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/oops/metadata.hpp --- a/src/share/vm/oops/metadata.hpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/oops/metadata.hpp Tue Mar 11 16:49:35 2014 -0700 @@ -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; } diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/opto/library_call.cpp --- a/src/share/vm/opto/library_call.cpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/opto/library_call.cpp Tue Mar 11 16:49:35 2014 -0700 @@ -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()); diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/prims/jvm.cpp --- a/src/share/vm/prims/jvm.cpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/prims/jvm.cpp Tue Mar 11 16:49:35 2014 -0700 @@ -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 */ diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/prims/jvmtiEnv.cpp --- a/src/share/vm/prims/jvmtiEnv.cpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/prims/jvmtiEnv.cpp Tue Mar 11 16:49:35 2014 -0700 @@ -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 */ diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/prims/jvmtiEnvBase.hpp --- a/src/share/vm/prims/jvmtiEnvBase.hpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/prims/jvmtiEnvBase.hpp Tue Mar 11 16:49:35 2014 -0700 @@ -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 { diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/prims/jvmtiEnvThreadState.cpp --- a/src/share/vm/prims/jvmtiEnvThreadState.cpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/prims/jvmtiEnvThreadState.cpp Tue Mar 11 16:49:35 2014 -0700 @@ -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; } diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/prims/jvmtiEventController.cpp --- a/src/share/vm/prims/jvmtiEventController.cpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/prims/jvmtiEventController.cpp Tue Mar 11 16:49:35 2014 -0700 @@ -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); } diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/prims/jvmtiThreadState.cpp --- a/src/share/vm/prims/jvmtiThreadState.cpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/prims/jvmtiThreadState.cpp Tue Mar 11 16:49:35 2014 -0700 @@ -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(); diff -r 3812c088b945 -r 74dd0c7b2de1 src/share/vm/runtime/vm_operations.hpp --- a/src/share/vm/runtime/vm_operations.hpp Thu Mar 06 10:37:26 2014 -0800 +++ b/src/share/vm/runtime/vm_operations.hpp Tue Mar 11 16:49:35 2014 -0700 @@ -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) \ diff -r 3812c088b945 -r 74dd0c7b2de1 test/runtime/6294277/SourceDebugExtension.java --- a/test/runtime/6294277/SourceDebugExtension.java Thu Mar 06 10:37:26 2014 -0800 +++ b/test/runtime/6294277/SourceDebugExtension.java Tue Mar 11 16:49:35 2014 -0700 @@ -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.*; diff -r 3812c088b945 -r 74dd0c7b2de1 test/serviceability/jvmti/TestRedefineWithUnresolvedClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/serviceability/jvmti/TestRedefineWithUnresolvedClass.java Tue Mar 11 16:49:35 2014 -0700 @@ -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); + } +} diff -r 3812c088b945 -r 74dd0c7b2de1 test/serviceability/jvmti/UnresolvedClassAgent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/serviceability/jvmti/UnresolvedClassAgent.java Tue Mar 11 16:49:35 2014 -0700 @@ -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)"); + } + } +} diff -r 3812c088b945 -r 74dd0c7b2de1 test/serviceability/jvmti/UnresolvedClassAgent.mf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/serviceability/jvmti/UnresolvedClassAgent.mf Tue Mar 11 16:49:35 2014 -0700 @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Premain-Class: UnresolvedClassAgent +Can-Redefine-Classes: true diff -r 3812c088b945 -r 74dd0c7b2de1 test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java --- a/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java Thu Mar 06 10:37:26 2014 -0800 +++ b/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java Tue Mar 11 16:49:35 2014 -0700 @@ -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(); + } } diff -r 3812c088b945 -r 74dd0c7b2de1 test/testlibrary/com/oracle/java/testlibrary/Utils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/Utils.java Tue Mar 11 16:49:35 2014 -0700 @@ -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 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 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 opts = new ArrayList(); + 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 opts = new ArrayList(); + 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 fileAsList(File file) throws IOException { + assertTrue(file.exists() && file.isFile(), + file.getAbsolutePath() + " does not exist or not a file"); + List output = new ArrayList<>(); + try (BufferedReader reader = new BufferedReader(new FileReader(file.getAbsolutePath()))) { + while (reader.ready()) { + output.add(reader.readLine().replace(NEW_LINE, "")); + } + } + return output; + } + +}