blob: f33afd148e800d6a0ea52e12a60f76908304cedf [file] [log] [blame]
/*
* Copyright (c) 2004, 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.
*/
package nsk.jvmti.scenarios.hotswap.HS201;
import java.io.*;
import java.util.*;
import nsk.share.*;
import nsk.share.jvmti.*;
/**
* The test implements the JVMTI <i>hotswap</i> scenario <code>HS201</code>:
* <ol><li> Enable events Exception, MethodExit, FramePop.</li>
* <li> Call method a() which calls method b().</li>
* <li> Throw an uncaught exception in the method b().</li>
* <li> Redefine a class with the changed methods a() and b() within
* triggered callbacks Exception, MethodExit, FramePop caused by
* the uncaught exception. New version contains the changed methods
* a() and b(). Both methods should stay obsolete during their
* execution.</li>
* </ol>
* The classfile redefinition and all checks are performed during the
* appropriate triggered callbacks. The methods are checked for obsolescence
* by analyzing length of the byte code array that implement the redefined
* method. The reason is that method ID from input parameters will be
* reasigned to the new method during execution of the very first callback but
* it will be already referred to the old method in the others subsequent
* callbacks in accordance with the JVMTI spec:
* <blockquote><i>An original method version which is not equivalent to
* the new method version is called obsolete and is assigned a new method
* ID; the original method ID now refers to the new method version</i></blockquote>
*/
public class hs201t003 extends DebugeeClass {
final static String REDEF_CLS_SIGNATURE =
"Lnsk/jvmti/scenarios/hotswap/HS201/hs201t003r;";
static int status = Consts.TEST_PASSED;
static Log log = null;
// path to the directory with redefining class files
static String clfBasePath = null;
// refer to a tested class to be redefined
hs201t003r redefCls;
native static void storeClassBytes(byte[] classBytes);
public static void main(String args[]) {
args = nsk.share.jvmti.JVMTITest.commonInit(args);
// produce JCK-like exit status.
System.exit(run(args, System.out) + Consts.JCK_STATUS_BASE);
}
public static int run(String args[], PrintStream out) {
return new hs201t003().runIt(args, out);
}
private int runIt(String args[], PrintStream out) {
int iter;
ArgumentHandler argHandler = new ArgumentHandler(args);
log = new Log(out, argHandler);
try {
// directory to search a redefining class
clfBasePath = args[0];
} catch(Exception e) {
throw new Failure("TEST BUG: Wrong test parameters, caught: "
+ e);
}
storeClassBytes(loadFromClassFile(REDEF_CLS_SIGNATURE));
// testing sync
log.display("waiting for the agent start ...\n");
status = checkStatus(status);
log.display("starting an auxiliary thread ...\n");
RedefClassWrapper redefClsWrapper = new RedefClassWrapper();
redefClsWrapper.setDaemon(true);
synchronized(redefClsWrapper) {
redefClsWrapper.start();
log.display("waiting for auxiliary thread readiness...\n");
try {
redefClsWrapper.wait(); // wait for the thread's readiness
} catch (InterruptedException e) {
redefClsWrapper.interrupt();
throw new Failure("TEST FAILURE: waiting for auxiliary thread start, caught: "
+ e);
}
}
// testing sync
log.display("auxiliary thread started\n"
+ "waiting for the agent finish ...\n");
status = checkStatus(status);
log.display("waiting for auxiliary thread ...\n");
redefClsWrapper.stopMe = true;
try {
redefClsWrapper.join();
} catch (InterruptedException e) {
redefClsWrapper.interrupt();
throw new Failure("TEST FAILURE: waiting for auxiliary thread death, caught: "
+ e);
}
// verify classfile redefinition
try {
redefCls.entryMethod();
} catch(Exception e) {
; // ignoring
}
if (redefCls.entryMethodFld == 0 || redefCls.entryMethod2Fld == 0) {
status = Consts.TEST_FAILED;
log.complain("TEST FAILED: tested class has not been redefined:\n"
+ "\tthe class fields still have old values: "
+ redefCls.entryMethodFld + " " + redefCls.entryMethodFld
+ "\n\texpected values: 1 1");
}
else
log.display("CHECK PASSED: tested class has been redefined");
return status;
}
/**
* Load bytes of a redefining class.
*/
private static byte[] loadFromClassFile(String signature) {
String testPath = clfBasePath + File.separator
+ "newclass" + File.separator
+ signature.substring(1, signature.length()-1).replace('/', File.separatorChar)
+ ".class";
File classFile = null;
log.display("looking for class file at\n\t"
+ testPath + " ...\n");
try {
classFile = new File(testPath);
} catch (NullPointerException e) {
throw new Failure("FAILURE: failed to open class file, caught: "
+ e);
}
log.display("loading " + classFile.length()
+ " bytes from class file "+ testPath + " ...\n");
byte[] buf = new byte[(int) classFile.length()];
try {
InputStream in = new FileInputStream(classFile);
in.read(buf);
in.close();
} catch (Exception e) {
throw new Failure("FAILURE: failed to load bytes from class file, caught: "
+ e);
}
log.display(classFile.length()
+ " bytes of a redefining class loaded\n");
return buf;
}
/**
* Class executing a class to be redefined.
*/
class RedefClassWrapper extends Thread {
boolean stopMe = false;
RedefClassWrapper() {
super("RedefClassWrapper");
}
public void run() {
log.display(this.getName() + ": started");
redefCls = new hs201t003r();
synchronized(this) {
this.notify(); // notify the main thread
while(!stopMe) {
try {
redefCls.entryMethod();
} catch(Exception e) {
break; // ignoring
}
// get the main thread chance to obtain CPU
try {
this.wait(100);
} catch(Exception e) {}
}
log.display(this.getName() + ": exiting");
}
}
}
/*************************************************************/
}
/**
* A class to be redefined in an agent.
*/
class hs201t003r {
// dummy fields used only to verify class file redefinition
public static int entryMethodFld = 0;
public static int entryMethod2Fld = 0;
void entryMethod() throws Exception {
entryMethod2();
}
void entryMethod2() throws Exception {
String str = "Tasya";
int i = 10;
i +=10;
i--;
String str2 = "Hi " + str;
throw new Exception("Test exception");
}
}