3ec12f2dc1
Do a better job of handling compilations before execution Reviewed-by: sspitsyn, amenkov
389 lines
14 KiB
Java
389 lines
14 KiB
Java
/*
|
|
* 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.RedefineClasses;
|
|
|
|
import java.io.*;
|
|
import java.util.*;
|
|
import nsk.share.*;
|
|
import nsk.share.jvmti.*;
|
|
|
|
/**
|
|
* The test exercises that the JVMTI function RedefineClasses()
|
|
* enable to redefine a local inner class properly. Redefiniton
|
|
* is performed in asynchronous manner from a separate thread when
|
|
* the VM is provoked to switch into compiled mode.<br>
|
|
* The test works as follows. Two threads are started. One is a java
|
|
* thread creating and executing a local class in an instance
|
|
* invoker method of the outer class. Second is a native thread
|
|
* executing an agent code. Then the local class method
|
|
* <code>redefclass030HotMethod()</code> is provoked to be compiled
|
|
* (optimized), and thus the JVMTI event <code>CompiledMethodLoad</code>
|
|
* should be sent. After that, the agent redefines the local class.
|
|
* Different kinds of outer fields and a local variable of the outer
|
|
* invoker method are accessed from executing methods in both versions
|
|
* of the local class. Upon the redefinition, the main test thread verifies
|
|
* via the outer fields/local variable values that the inner methods
|
|
* have been redefined. It also verifies that the outer class
|
|
* is still can access the local class fields after the redefinition.
|
|
*/
|
|
public class redefclass030 extends DebugeeClass {
|
|
final static String REDEF_CLS_SIGNATURE =
|
|
"Lnsk/jvmti/RedefineClasses/redefclass030$RedefClassWrapper$1RedefClass;";
|
|
|
|
static int status = Consts.TEST_PASSED;
|
|
|
|
static Log log = null;
|
|
|
|
// path to the directory with redefining class files
|
|
static String clfBasePath = null;
|
|
|
|
// dummy "outer-outer" fields to be changed by the local class
|
|
private int prOuterOuterFl[] = {0,0};
|
|
int packOuterOuterFl[] = {0,0};
|
|
public int pubOuterOuterFl[] = {0,0};
|
|
private static int prStOuterOuterFl[] = {0,0};
|
|
static int packStOuterOuterFl[] = {0,0};
|
|
public static int pubStOuterOuterFl[] = {0,0};
|
|
|
|
native static void storeClassBytes(byte[] classBytes);
|
|
native static void notifyNativeAgent(); /** notify native agent that "hot" method is entered */
|
|
native static boolean isRedefinitionOccurred(); /** check whether class redefinition was already occurred */
|
|
|
|
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 redefclass030().runIt(args, out);
|
|
}
|
|
|
|
private int runIt(String args[], PrintStream out) {
|
|
int iter;
|
|
|
|
ArgumentHandler argHandler = new ArgumentHandler(args);
|
|
log = new Log(out, argHandler);
|
|
|
|
try {
|
|
// number of iterations for a method to become 'hotspot' one
|
|
iter = Integer.parseInt(args[0]);
|
|
|
|
// directory to search a redefining class
|
|
clfBasePath = args[1];
|
|
} 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(iter);
|
|
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);
|
|
|
|
boolean isRedefinitionStarted = waitForRedefinitionStarted();
|
|
boolean isRedefinitionCompleted = false;
|
|
if (isRedefinitionStarted) {
|
|
isRedefinitionCompleted = waitForRedefinitionCompleted(redefClsWrapper);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// CR 6604375: check whether class redefinition occurred
|
|
if (isRedefinitionCompleted) {
|
|
// verify results
|
|
checkOuterOuterFields(0, 2);
|
|
checkOuterOuterFields(1, 2);
|
|
checkOuterFields(redefClsWrapper, 0, 2);
|
|
checkOuterFields(redefClsWrapper, 1, 2);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
private boolean waitForRedefinitionStarted() {
|
|
final int SLEEP_MS = 20;
|
|
int iterationsLeft = 2000 / SLEEP_MS;
|
|
while (iterationsLeft >= 0) {
|
|
if (isRedefinitionOccurred()) {
|
|
log.display("Redefinition started.");
|
|
return true;
|
|
}
|
|
--iterationsLeft;
|
|
safeSleep(SLEEP_MS);
|
|
}
|
|
log.complain("Redefinition not started. May need more time for -Xcomp.");
|
|
status = Consts.TEST_FAILED;
|
|
return false;
|
|
}
|
|
|
|
private boolean waitForRedefinitionCompleted(RedefClassWrapper redefClsWrapper) {
|
|
final int SLEEP_MS = 20;
|
|
int iterationsLeft = 10000 / SLEEP_MS;
|
|
while (iterationsLeft >= 0) {
|
|
// Check if new code has changed fields.
|
|
if (prStOuterOuterFl[0] == 2 && prStOuterOuterFl[1] == 2 && redefClsWrapper.prOuterFl[1] == 2) {
|
|
log.display("Redefinition completed.");
|
|
return true;
|
|
}
|
|
--iterationsLeft;
|
|
safeSleep(SLEEP_MS);
|
|
}
|
|
log.complain("Redefinition not completed. May need more time for -Xcomp.");
|
|
status = Consts.TEST_FAILED;
|
|
return false;
|
|
}
|
|
|
|
private void checkOuterOuterFields(int index, int expValue) {
|
|
if (prOuterOuterFl[index] != expValue
|
|
|| packOuterOuterFl[index] != expValue
|
|
|| pubOuterOuterFl[index] != expValue
|
|
|| prStOuterOuterFl[index] != expValue
|
|
|| packStOuterOuterFl[index] != expValue
|
|
|| pubStOuterOuterFl[index] != expValue) {
|
|
status = Consts.TEST_FAILED;
|
|
log.complain("TEST FAILED: unexpected values of outer fields of the class"
|
|
+ "\n\t\"" + this.toString() + "\":"
|
|
+ "\n\t\tprOuterOuterFl["+ index +"]: got: " + prOuterOuterFl[index]
|
|
+ ", expected: " + expValue
|
|
+ "\n\t\tpackOuterOuterFl["+ index +"]: got: " + packOuterOuterFl[index]
|
|
+ ", expected: " + expValue
|
|
+ "\n\t\tpubOuterOuterFl["+ index +"]: got: " + pubOuterOuterFl[index]
|
|
+ ", expected: " + expValue
|
|
|
|
+ "\n\t\tprStOuterOuterFl["+ index +"]: got: " + prStOuterOuterFl[index]
|
|
+ ", expected: " + expValue
|
|
+ "\n\t\tpackStOuterOuterFl["+ index +"]: got: " + packStOuterOuterFl[index]
|
|
+ ", expected: " + expValue
|
|
+ "\n\t\tpubStOuterOuterFl["+ index +"]: got: " + pubStOuterOuterFl[index]
|
|
+ ", expected: " + expValue);
|
|
}
|
|
}
|
|
|
|
private void checkOuterFields(RedefClassWrapper redefClsWrapper, int index, int expValue) {
|
|
if (redefClsWrapper.prOuterFl[index] != expValue
|
|
|| redefClsWrapper.packOuterFl[index] != expValue
|
|
|| redefClsWrapper.pubOuterFl[index] != expValue) {
|
|
status = Consts.TEST_FAILED;
|
|
log.complain("TEST FAILED: unexpected values of outer fields of the class"
|
|
+ "\n\t\"" + redefClsWrapper.toString() + "\":"
|
|
+ "\n\t\tprOuterFl["+ index +"]: got: " + redefClsWrapper.prOuterFl[index]
|
|
+ ", expected: " + expValue
|
|
+ "\n\t\tpackOuterFl["+ index +"]: got: " + redefClsWrapper.packOuterFl[index]
|
|
+ ", expected: " + expValue
|
|
+ "\n\t\tpubOuterFl["+ index +"]: got: " + redefClsWrapper.pubOuterFl[index]
|
|
+ ", expected: " + expValue);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 the local inner class to be redefined.
|
|
*/
|
|
class RedefClassWrapper extends Thread {
|
|
boolean stopMe = false;
|
|
int iter;
|
|
|
|
// dummy outer fields to be changed by the local class
|
|
private int prOuterFl[] = {0,0};
|
|
int packOuterFl[] = {0,0};
|
|
public int pubOuterFl[] = {0,0};
|
|
|
|
RedefClassWrapper(int iter) {
|
|
super("RedefClassWrapper");
|
|
this.iter = iter;
|
|
}
|
|
|
|
public void run() {
|
|
// dummy local vars to be changed by the local class
|
|
final int outerLocalVar[] = {0,0};
|
|
|
|
/**
|
|
* Local inner class to be redefined in an agent.
|
|
*/
|
|
class RedefClass {
|
|
int iter;
|
|
|
|
// dummy inner fields to be accessed by the outer class
|
|
private int prInnerFl = 1;
|
|
int packInnerFl = 1;
|
|
public int pubInnerFl = 1;
|
|
|
|
RedefClass(int iter) {
|
|
this.iter = iter;
|
|
}
|
|
|
|
void warmUpMethod() {
|
|
prOuterOuterFl[0] = 1;
|
|
packOuterOuterFl[0] = 1;
|
|
pubOuterOuterFl[0] = 1;
|
|
prStOuterOuterFl[0] = 1;
|
|
packStOuterOuterFl[0] = 1;
|
|
pubStOuterOuterFl[0] = 1;
|
|
|
|
prOuterFl[0] = 1;
|
|
packOuterFl[0] = 1;
|
|
pubOuterFl[0] = 1;
|
|
|
|
outerLocalVar[0] = 1;
|
|
|
|
for (int i=0; i<iter; i++)
|
|
redefclass030HotMethod(i);
|
|
|
|
redefclass030HotMethod(10);
|
|
}
|
|
|
|
/**
|
|
* Hotspot method to be compiled.
|
|
*/
|
|
void redefclass030HotMethod(int i) {
|
|
prOuterOuterFl[1] = 1;
|
|
packOuterOuterFl[1] = 1;
|
|
pubOuterOuterFl[1] = 1;
|
|
prStOuterOuterFl[1] = 1;
|
|
packStOuterOuterFl[1] = 1;
|
|
pubStOuterOuterFl[1] = 1;
|
|
|
|
prOuterFl[1] = 1;
|
|
packOuterFl[1] = 1;
|
|
pubOuterFl[1] = 1;
|
|
|
|
outerLocalVar[1] = 1;
|
|
|
|
int j=0;
|
|
|
|
j +=i;
|
|
j--;
|
|
if (j >10)
|
|
j = 0;
|
|
|
|
notifyNativeAgent();
|
|
}
|
|
}
|
|
/*************************************************************/
|
|
|
|
log.display(this.getName() + ": started");
|
|
|
|
RedefClass redefCls = new RedefClass(iter);
|
|
|
|
synchronized(this) {
|
|
this.notify(); // notify the main thread
|
|
|
|
while(!stopMe) {
|
|
redefCls.warmUpMethod();
|
|
|
|
// get the main thread chance to obtain CPU
|
|
try {
|
|
this.wait(100);
|
|
} catch(Exception e) {}
|
|
}
|
|
|
|
if (redefCls.prInnerFl != 1
|
|
|| redefCls.packInnerFl != 1
|
|
|| redefCls.pubInnerFl != 1) {
|
|
status = Consts.TEST_FAILED;
|
|
log.complain("TEST FAILED: unexpected values of inner fields of the local class"
|
|
+ "\n\t\"" + this.toString() + "\":"
|
|
+ "\n\t\tprInnerFl: got: " + redefCls.prInnerFl
|
|
+ ", expected: 1"
|
|
+ "\n\t\tpackInnerFl: got: " + redefCls.packInnerFl
|
|
+ ", expected: 1"
|
|
+ "\n\t\tpubInnerFl: got: " + redefCls.pubInnerFl
|
|
+ ", expected: 1");
|
|
}
|
|
|
|
log.display(this.getName() + ": exiting");
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|