diff --git a/test/java/lang/instrument/RedefineMethodInBacktrace.sh b/test/java/lang/instrument/RedefineMethodInBacktrace.sh new file mode 100644 index 0000000000000000000000000000000000000000..20167bf8330d4e575ecb5629a78eb39b1441ce94 --- /dev/null +++ b/test/java/lang/instrument/RedefineMethodInBacktrace.sh @@ -0,0 +1,87 @@ +# +# 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. +# + +# @test +# @bug 7174978 +# @summary Redefine a class with a method stored in a backtrace. +# @author Stefan Karlsson +# +# @run shell MakeJAR3.sh RedefineMethodInBacktraceAgent 'Can-Redefine-Classes: true' +# @run build RedefineMethodInBacktraceTarget RedefineMethodInBacktraceApp +# @run shell RedefineMethodInBacktrace.sh +# + +if [ "${TESTJAVA}" = "" ] +then + echo "TESTJAVA not set. Test cannot execute. Failed." + exit 1 +fi + +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + +if [ "${TESTSRC}" = "" ] +then + echo "TESTSRC not set. Test cannot execute. Failed." + exit 1 +fi + +if [ "${TESTCLASSES}" = "" ] +then + echo "TESTCLASSES not set. Test cannot execute. Failed." + exit 1 +fi + +JAVAC="${COMPILEJAVA}"/bin/javac +JAVA="${TESTJAVA}"/bin/java + +cp "${TESTSRC}"/RedefineMethodInBacktraceTarget_2.java \ + RedefineMethodInBacktraceTarget.java +"${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . RedefineMethodInBacktraceTarget.java + +"${JAVA}" ${TESTVMOPTS} -javaagent:RedefineMethodInBacktraceAgent.jar \ + -classpath "${TESTCLASSES}" RedefineMethodInBacktraceApp > output.log 2>&1 +RUN_RESULT=$? + +if [ $RUN_RESULT != 0 ]; then + echo "FAIL: the run failed with exit code '$RUN_RESULT'" + exit $RUN_RESULT +fi + +cat output.log + +MESG="Exception" +grep "$MESG" output.log +result=$? +if [ "$result" = 0 ]; then + echo "FAIL: found '$MESG' in the test output" + result=1 +else + echo "PASS: did NOT find '$MESG' in the test output" + result=0 +fi + +exit $result diff --git a/test/java/lang/instrument/RedefineMethodInBacktraceAgent.java b/test/java/lang/instrument/RedefineMethodInBacktraceAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..a4cc182bf237313c115021cfd2bb065b9ca67c6d --- /dev/null +++ b/test/java/lang/instrument/RedefineMethodInBacktraceAgent.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +import java.lang.instrument.Instrumentation; + +public class RedefineMethodInBacktraceAgent { + private static Instrumentation instrumentation; + + private RedefineMethodInBacktraceAgent() {} + + public static void premain(String agentArgs, Instrumentation inst) { + System.out.println("Hello from RedefineMethodInBacktraceAgent!"); + System.out.println("isRedefineClassesSupported()=" + + inst.isRedefineClassesSupported()); + + instrumentation = inst; + } + + public static Instrumentation getInstrumentation() { + return instrumentation; + } +} diff --git a/test/java/lang/instrument/RedefineMethodInBacktraceApp.java b/test/java/lang/instrument/RedefineMethodInBacktraceApp.java new file mode 100644 index 0000000000000000000000000000000000000000..a4ad08846617dea7b8634f4e203e3c9607519e78 --- /dev/null +++ b/test/java/lang/instrument/RedefineMethodInBacktraceApp.java @@ -0,0 +1,111 @@ +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.lang.instrument.ClassDefinition; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * When an exception is thrown, the JVM collects just enough information + * about the stack trace to be able to create a full fledged stack trace + * (StackTraceElement[]). The backtrace contains this information and the + * JVM must make sure that the data in the backtrace is still usable after + * a class redefinition. + * + * After the PermGen removal there was a bug when the last reference to a Method + * was in the backtrace. The class of the method was kept alive, because of the + * mirror stored in the backtrace, but the old versions of the redefined method + * could be freed, since class redefinition didn't know about the backtraces. + */ +public class RedefineMethodInBacktraceApp { + public static void main(String args[]) throws Exception { + System.out.println("Hello from RedefineMethodInBacktraceApp!"); + + new RedefineMethodInBacktraceApp().doTest(); + + System.exit(0); + } + + private void doTest() throws Exception { + doMethodInBacktraceTest(); + } + + private void doMethodInBacktraceTest() throws Exception { + Throwable t = getThrowableFromMethodToRedefine(); + + doRedefine(RedefineMethodInBacktraceTarget.class); + + doClassUnloading(); + + touchRedefinedMethodInBacktrace(t); + } + + private static Throwable getThrowableFromMethodToRedefine() throws Exception { + Class c = + RedefineMethodInBacktraceTarget.class; + Method method = c.getMethod("methodToRedefine"); + + Throwable thrownFromMethodToRedefine = null; + try { + method.invoke(null); + } catch (InvocationTargetException e) { + thrownFromMethodToRedefine = e.getCause(); + if (!(thrownFromMethodToRedefine instanceof RuntimeException)) { + throw e; + } + } + method = null; + c = null; + + return thrownFromMethodToRedefine; + } + + private static void doClassUnloading() { + // This will clean out old, unused redefined methods. + System.gc(); + } + + private static void touchRedefinedMethodInBacktrace(Throwable throwable) { + // Make sure that we can convert the backtrace, which is referring to + // the redefined method, to a StrackTraceElement[] without crashing. + throwable.getStackTrace(); + } + + private static void doRedefine(Class clazz) throws Exception { + // Load the second version of this class. + File f = new File(clazz.getName() + ".class"); + System.out.println("Reading test class from " + f.getAbsolutePath()); + InputStream redefineStream = new FileInputStream(f); + + byte[] redefineBuffer = NamedBuffer.loadBufferFromStream(redefineStream); + + ClassDefinition redefineParamBlock = new ClassDefinition( + clazz, redefineBuffer); + + RedefineMethodInBacktraceAgent.getInstrumentation().redefineClasses( + new ClassDefinition[] {redefineParamBlock}); + } +} diff --git a/test/java/lang/instrument/RedefineMethodInBacktraceTarget.java b/test/java/lang/instrument/RedefineMethodInBacktraceTarget.java new file mode 100644 index 0000000000000000000000000000000000000000..fad881137c86e3b1b2c5fe38c74721c762c5f367 --- /dev/null +++ b/test/java/lang/instrument/RedefineMethodInBacktraceTarget.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * The first version of this class. The second version is in + * RedefineMethodInBacktraceTarget_2.java. + */ +public class RedefineMethodInBacktraceTarget { + public static void methodToRedefine() { + throw new RuntimeException("Test exception"); + } +} diff --git a/test/java/lang/instrument/RedefineMethodInBacktraceTarget_2.java b/test/java/lang/instrument/RedefineMethodInBacktraceTarget_2.java new file mode 100644 index 0000000000000000000000000000000000000000..d2ba2259932eb8421011556bc19ccee8eec4207e --- /dev/null +++ b/test/java/lang/instrument/RedefineMethodInBacktraceTarget_2.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * This is the second version of this class. The first version is in + * RedefineMethodInBacktraceTarget.java. + */ +public class RedefineMethodInBacktraceTarget { + public static void methodToRedefine() { + throw new RuntimeException("Test exception 2"); + } +}