--- a/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp Sat Apr 07 09:06:55 2012 -0700
+++ b/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp Mon Apr 09 08:38:16 2012 -0700
@@ -2651,56 +2651,49 @@
// Check to see if a field modification watch has been set before we take
// the time to call into the VM.
Label L2;
- __ mov32(rcx, ExternalAddress((address)JvmtiExport::get_field_modification_count_addr()));
- __ testl(rcx,rcx);
- __ jcc(Assembler::zero, L2);
- __ pop_ptr(rbx); // copy the object pointer from tos
- __ verify_oop(rbx);
- __ push_ptr(rbx); // put the object pointer back on tos
- __ subptr(rsp, sizeof(jvalue)); // add space for a jvalue object
- __ mov(rcx, rsp);
- __ push_ptr(rbx); // save object pointer so we can steal rbx,
- __ xorptr(rbx, rbx);
- const Address lo_value(rcx, rbx, Address::times_1, 0*wordSize);
- const Address hi_value(rcx, rbx, Address::times_1, 1*wordSize);
- switch (bytecode()) { // load values into the jvalue object
- case Bytecodes::_fast_bputfield: __ movb(lo_value, rax); break;
- case Bytecodes::_fast_sputfield: __ movw(lo_value, rax); break;
- case Bytecodes::_fast_cputfield: __ movw(lo_value, rax); break;
- case Bytecodes::_fast_iputfield: __ movl(lo_value, rax); break;
- case Bytecodes::_fast_lputfield:
- NOT_LP64(__ movptr(hi_value, rdx));
- __ movptr(lo_value, rax);
- break;
-
- // need to call fld_s() after fstp_s() to restore the value for below
- case Bytecodes::_fast_fputfield: __ fstp_s(lo_value); __ fld_s(lo_value); break;
-
- // need to call fld_d() after fstp_d() to restore the value for below
- case Bytecodes::_fast_dputfield: __ fstp_d(lo_value); __ fld_d(lo_value); break;
-
- // since rcx is not an object we don't call store_check() here
- case Bytecodes::_fast_aputfield: __ movptr(lo_value, rax); break;
-
- default: ShouldNotReachHere();
- }
- __ pop_ptr(rbx); // restore copy of object pointer
-
- // Save rax, and sometimes rdx because call_VM() will clobber them,
- // then use them for JVM/DI purposes
- __ push(rax);
- if (bytecode() == Bytecodes::_fast_lputfield) __ push(rdx);
- // access constant pool cache entry
- __ get_cache_entry_pointer_at_bcp(rax, rdx, 1);
- __ verify_oop(rbx);
- // rbx,: object pointer copied above
- // rax,: cache entry pointer
- // rcx: jvalue object on the stack
- __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), rbx, rax, rcx);
- if (bytecode() == Bytecodes::_fast_lputfield) __ pop(rdx); // restore high value
- __ pop(rax); // restore lower value
- __ addptr(rsp, sizeof(jvalue)); // release jvalue object space
- __ bind(L2);
+ __ mov32(rcx, ExternalAddress((address)JvmtiExport::get_field_modification_count_addr()));
+ __ testl(rcx,rcx);
+ __ jcc(Assembler::zero, L2);
+ __ pop_ptr(rbx); // copy the object pointer from tos
+ __ verify_oop(rbx);
+ __ push_ptr(rbx); // put the object pointer back on tos
+
+ // Save tos values before call_VM() clobbers them. Since we have
+ // to do it for every data type, we use the saved values as the
+ // jvalue object.
+ switch (bytecode()) { // load values into the jvalue object
+ case Bytecodes::_fast_aputfield: __ push_ptr(rax); break;
+ case Bytecodes::_fast_bputfield: // fall through
+ case Bytecodes::_fast_sputfield: // fall through
+ case Bytecodes::_fast_cputfield: // fall through
+ case Bytecodes::_fast_iputfield: __ push_i(rax); break;
+ case Bytecodes::_fast_dputfield: __ push_d(); break;
+ case Bytecodes::_fast_fputfield: __ push_f(); break;
+ case Bytecodes::_fast_lputfield: __ push_l(rax); break;
+
+ default:
+ ShouldNotReachHere();
+ }
+ __ mov(rcx, rsp); // points to jvalue on the stack
+ // access constant pool cache entry
+ __ get_cache_entry_pointer_at_bcp(rax, rdx, 1);
+ __ verify_oop(rbx);
+ // rbx,: object pointer copied above
+ // rax,: cache entry pointer
+ // rcx: jvalue object on the stack
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), rbx, rax, rcx);
+
+ switch (bytecode()) { // restore tos values
+ case Bytecodes::_fast_aputfield: __ pop_ptr(rax); break;
+ case Bytecodes::_fast_bputfield: // fall through
+ case Bytecodes::_fast_sputfield: // fall through
+ case Bytecodes::_fast_cputfield: // fall through
+ case Bytecodes::_fast_iputfield: __ pop_i(rax); break;
+ case Bytecodes::_fast_dputfield: __ pop_d(); break;
+ case Bytecodes::_fast_fputfield: __ pop_f(); break;
+ case Bytecodes::_fast_lputfield: __ pop_l(rax); break;
+ }
+ __ bind(L2);
}
}
--- a/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp Sat Apr 07 09:06:55 2012 -0700
+++ b/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp Mon Apr 09 08:38:16 2012 -0700
@@ -2685,26 +2685,23 @@
__ pop_ptr(rbx); // copy the object pointer from tos
__ verify_oop(rbx);
__ push_ptr(rbx); // put the object pointer back on tos
- __ subptr(rsp, sizeof(jvalue)); // add space for a jvalue object
- __ mov(c_rarg3, rsp);
- const Address field(c_rarg3, 0);
-
+ // Save tos values before call_VM() clobbers them. Since we have
+ // to do it for every data type, we use the saved values as the
+ // jvalue object.
switch (bytecode()) { // load values into the jvalue object
- case Bytecodes::_fast_aputfield: __ movq(field, rax); break;
- case Bytecodes::_fast_lputfield: __ movq(field, rax); break;
- case Bytecodes::_fast_iputfield: __ movl(field, rax); break;
- case Bytecodes::_fast_bputfield: __ movb(field, rax); break;
+ case Bytecodes::_fast_aputfield: __ push_ptr(rax); break;
+ case Bytecodes::_fast_bputfield: // fall through
case Bytecodes::_fast_sputfield: // fall through
- case Bytecodes::_fast_cputfield: __ movw(field, rax); break;
- case Bytecodes::_fast_fputfield: __ movflt(field, xmm0); break;
- case Bytecodes::_fast_dputfield: __ movdbl(field, xmm0); break;
+ case Bytecodes::_fast_cputfield: // fall through
+ case Bytecodes::_fast_iputfield: __ push_i(rax); break;
+ case Bytecodes::_fast_dputfield: __ push_d(); break;
+ case Bytecodes::_fast_fputfield: __ push_f(); break;
+ case Bytecodes::_fast_lputfield: __ push_l(rax); break;
+
default:
ShouldNotReachHere();
}
-
- // Save rax because call_VM() will clobber it, then use it for
- // JVMTI purposes
- __ push(rax);
+ __ mov(c_rarg3, rsp); // points to jvalue on the stack
// access constant pool cache entry
__ get_cache_entry_pointer_at_bcp(c_rarg2, rax, 1);
__ verify_oop(rbx);
@@ -2715,8 +2712,17 @@
CAST_FROM_FN_PTR(address,
InterpreterRuntime::post_field_modification),
rbx, c_rarg2, c_rarg3);
- __ pop(rax); // restore lower value
- __ addptr(rsp, sizeof(jvalue)); // release jvalue object space
+
+ switch (bytecode()) { // restore tos values
+ case Bytecodes::_fast_aputfield: __ pop_ptr(rax); break;
+ case Bytecodes::_fast_bputfield: // fall through
+ case Bytecodes::_fast_sputfield: // fall through
+ case Bytecodes::_fast_cputfield: // fall through
+ case Bytecodes::_fast_iputfield: __ pop_i(rax); break;
+ case Bytecodes::_fast_dputfield: __ pop_d(); break;
+ case Bytecodes::_fast_fputfield: __ pop_f(); break;
+ case Bytecodes::_fast_lputfield: __ pop_l(rax); break;
+ }
__ bind(L2);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/7158988/FieldMonitor.java Mon Apr 09 08:38:16 2012 -0700
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2012 SAP AG. 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 FieldMonitor.java
+ * @bug 7158988
+ * @summary verify jvm does not crash while debugging
+ * @run shell TestFieldMonitor.sh
+ * @author axel.siebenborn@sap.com
+ */
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.Field;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.connect.Connector;
+import com.sun.jdi.connect.IllegalConnectorArgumentsException;
+import com.sun.jdi.connect.LaunchingConnector;
+import com.sun.jdi.connect.VMStartException;
+import com.sun.jdi.event.ClassPrepareEvent;
+import com.sun.jdi.event.Event;
+import com.sun.jdi.event.EventQueue;
+import com.sun.jdi.event.EventSet;
+import com.sun.jdi.event.ModificationWatchpointEvent;
+import com.sun.jdi.event.VMDeathEvent;
+import com.sun.jdi.event.VMDisconnectEvent;
+import com.sun.jdi.request.ClassPrepareRequest;
+import com.sun.jdi.request.EventRequest;
+import com.sun.jdi.request.EventRequestManager;
+import com.sun.jdi.request.ModificationWatchpointRequest;
+
+public class FieldMonitor {
+
+ public static final String CLASS_NAME = "TestPostFieldModification";
+ public static final String FIELD_NAME = "value";
+ public static final String ARGUMENTS = "-Xshare:off -XX:+PrintGC";
+
+ public static void main(String[] args)
+ throws IOException, InterruptedException {
+
+ StringBuffer sb = new StringBuffer();
+
+ for (int i=0; i < args.length; i++) {
+ sb.append(' ');
+ sb.append(args[i]);
+ }
+ //VirtualMachine vm = launchTarget(sb.toString());
+ VirtualMachine vm = launchTarget(CLASS_NAME);
+
+ System.out.println("Vm launched");
+ // set watch field on already loaded classes
+ List<ReferenceType> referenceTypes = vm
+ .classesByName(CLASS_NAME);
+ for (ReferenceType refType : referenceTypes) {
+ addFieldWatch(vm, refType);
+ }
+ // watch for loaded classes
+ addClassWatch(vm);
+
+ // process events
+ EventQueue eventQueue = vm.eventQueue();
+ // resume the vm
+
+ Process process = vm.process();
+
+
+ // Copy target's output and error to our output and error.
+ Thread outThread = new StreamRedirectThread("out reader", process.getInputStream());
+ Thread errThread = new StreamRedirectThread("error reader", process.getErrorStream());
+
+ errThread.start();
+ outThread.start();
+
+
+ vm.resume();
+ boolean connected = true;
+ while (connected) {
+ EventSet eventSet = eventQueue.remove();
+ for (Event event : eventSet) {
+ if (event instanceof VMDeathEvent
+ || event instanceof VMDisconnectEvent) {
+ // exit
+ connected = false;
+ } else if (event instanceof ClassPrepareEvent) {
+ // watch field on loaded class
+ System.out.println("ClassPrepareEvent");
+ ClassPrepareEvent classPrepEvent = (ClassPrepareEvent) event;
+ ReferenceType refType = classPrepEvent
+ .referenceType();
+ addFieldWatch(vm, refType);
+ } else if (event instanceof ModificationWatchpointEvent) {
+ System.out.println("sleep for 500 ms");
+ Thread.sleep(500);
+ System.out.println("resume...");
+
+ ModificationWatchpointEvent modEvent = (ModificationWatchpointEvent) event;
+ System.out.println("old="
+ + modEvent.valueCurrent());
+ System.out.println("new=" + modEvent.valueToBe());
+ System.out.println();
+ }
+ }
+ eventSet.resume();
+ }
+ // Shutdown begins when event thread terminates
+ try {
+ errThread.join(); // Make sure output is forwarded
+ outThread.join();
+ } catch (InterruptedException exc) {
+ // we don't interrupt
+ }
+ }
+
+ /**
+ * Find a com.sun.jdi.CommandLineLaunch connector
+ */
+ static LaunchingConnector findLaunchingConnector() {
+ List <Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();
+ Iterator <Connector> iter = connectors.iterator();
+ while (iter.hasNext()) {
+ Connector connector = iter.next();
+ if (connector.name().equals("com.sun.jdi.CommandLineLaunch")) {
+ return (LaunchingConnector)connector;
+ }
+ }
+ throw new Error("No launching connector");
+ }
+ /**
+ * Return the launching connector's arguments.
+ */
+ static Map <String,Connector.Argument> connectorArguments(LaunchingConnector connector, String mainArgs) {
+ Map<String,Connector.Argument> arguments = connector.defaultArguments();
+ for (String key : arguments.keySet()) {
+ System.out.println(key);
+ }
+
+ Connector.Argument mainArg = (Connector.Argument)arguments.get("main");
+ if (mainArg == null) {
+ throw new Error("Bad launching connector");
+ }
+ mainArg.setValue(mainArgs);
+
+ Connector.Argument optionsArg = (Connector.Argument)arguments.get("options");
+ if (optionsArg == null) {
+ throw new Error("Bad launching connector");
+ }
+ optionsArg.setValue(ARGUMENTS);
+ return arguments;
+ }
+
+ static VirtualMachine launchTarget(String mainArgs) {
+ LaunchingConnector connector = findLaunchingConnector();
+ Map arguments = connectorArguments(connector, mainArgs);
+ try {
+ return (VirtualMachine) connector.launch(arguments);
+ } catch (IOException exc) {
+ throw new Error("Unable to launch target VM: " + exc);
+ } catch (IllegalConnectorArgumentsException exc) {
+ throw new Error("Internal error: " + exc);
+ } catch (VMStartException exc) {
+ throw new Error("Target VM failed to initialize: " +
+ exc.getMessage());
+ }
+}
+
+
+ private static void addClassWatch(VirtualMachine vm) {
+ EventRequestManager erm = vm.eventRequestManager();
+ ClassPrepareRequest classPrepareRequest = erm
+ .createClassPrepareRequest();
+ classPrepareRequest.addClassFilter(CLASS_NAME);
+ classPrepareRequest.setEnabled(true);
+ }
+
+
+ private static void addFieldWatch(VirtualMachine vm,
+ ReferenceType refType) {
+ EventRequestManager erm = vm.eventRequestManager();
+ Field field = refType.fieldByName(FIELD_NAME);
+ ModificationWatchpointRequest modificationWatchpointRequest = erm
+ .createModificationWatchpointRequest(field);
+ modificationWatchpointRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
+ modificationWatchpointRequest.setEnabled(true);
+ }
+}
+
+class StreamRedirectThread extends Thread {
+
+ private final BufferedReader in;
+
+ private static final int BUFFER_SIZE = 2048;
+
+ /**
+ * Set up for copy.
+ * @param name Name of the thread
+ * @param in Stream to copy from
+ * @param out Stream to copy to
+ */
+ StreamRedirectThread(String name, InputStream in) {
+ super(name);
+ this.in = new BufferedReader(new InputStreamReader(in));
+ }
+
+ /**
+ * Copy.
+ */
+ public void run() {
+ try {
+ String line;
+ while ((line = in.readLine ()) != null) {
+ System.out.println ("testvm: " + line);
+ }
+ System.out.flush();
+ } catch(IOException exc) {
+ System.err.println("Child I/O Transfer - " + exc);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/7158988/TestFieldMonitor.sh Mon Apr 09 08:38:16 2012 -0700
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+if [ "${TESTSRC}" = "" ]
+then TESTSRC=.
+fi
+
+if [ "${TESTJAVA}" = "" ]
+then
+ PARENT=`dirname \`which java\``
+ TESTJAVA=`dirname ${PARENT}`
+ echo "TESTJAVA not set, selecting " ${TESTJAVA}
+ echo "If this is incorrect, try setting the variable manually."
+fi
+
+if [ "${TESTCLASSES}" = "" ]
+then
+ echo "TESTCLASSES not set. Test cannot execute. Failed."
+ exit 1
+fi
+
+BIT_FLAG=""
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+ SunOS | Linux )
+ NULL=/dev/null
+ PS=":"
+ FS="/"
+ ## for solaris, linux it's HOME
+ FILE_LOCATION=$HOME
+ if [ -f ${FILE_LOCATION}${FS}JDK64BIT -a ${OS} = "SunOS" -a `uname -p`='sparc' ]
+ then
+ BIT_FLAG="-d64"
+ fi
+ ;;
+ Windows_95 | Windows_98 | Windows_ME )
+ NULL=NUL
+ PS=";"
+ FS="\\"
+ echo "Test skipped, only for WinNT"
+ exit 0
+ ;;
+ Windows_NT )
+ NULL=NUL
+ PS=";"
+ FS="\\"
+ ;;
+ * )
+ echo "Unrecognized system!"
+ exit 1;
+ ;;
+esac
+
+#CLASSPATH=.${PS}${TESTCLASSES} ; export CLASSPATH
+
+cp ${TESTSRC}${FS}*.java .
+
+${TESTJAVA}${FS}bin${FS}java ${BIT_FLAG} -fullversion
+
+${TESTJAVA}${FS}bin${FS}javac -classpath .${PS}$TESTJAVA${FS}lib${FS}tools.jar *.java
+
+${TESTJAVA}${FS}bin${FS}java ${BIT_FLAG} -classpath .${PS}$TESTJAVA${FS}lib${FS}tools.jar FieldMonitor > test.out 2>&1 &
+
+P_PID=$!
+
+sleep 60
+STATUS=0
+
+case "$OS" in
+ SunOS | Linux )
+ ps -ef | grep $P_PID | grep -v grep > ${NULL}
+ if [ $? = 0 ]; then
+ kill -9 $P_PID
+ STATUS=1
+ fi
+ ;;
+ * )
+ ps | grep -i "FieldMonitor" | grep -v grep > ${NULL}
+ if [ $? = 0 ]; then
+ C_PID=`ps | grep -i "FieldMonitor" | awk '{print $1}'`
+ kill -s 9 $C_PID
+ STATUS=1
+ fi
+ ;;
+esac
+
+grep "A fatal error has been detected" test.out > ${NULL}
+if [ $? = 0 ]; then
+ cat test.out
+ STATUS=1
+fi
+
+exit $STATUS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/7158988/TestPostFieldModification.java Mon Apr 09 08:38:16 2012 -0700
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2012 SAP AG. 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 FieldMonitor.java
+ * @bug 7158988
+ * @summary verify jvm does not crash while debugging
+ * @run shell TestFieldMonitor.sh
+ * @author axel.siebenborn@sap.com
+ */
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.Field;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.connect.Connector;
+import com.sun.jdi.connect.IllegalConnectorArgumentsException;
+import com.sun.jdi.connect.LaunchingConnector;
+import com.sun.jdi.connect.VMStartException;
+import com.sun.jdi.event.ClassPrepareEvent;
+import com.sun.jdi.event.Event;
+import com.sun.jdi.event.EventQueue;
+import com.sun.jdi.event.EventSet;
+import com.sun.jdi.event.ModificationWatchpointEvent;
+import com.sun.jdi.event.VMDeathEvent;
+import com.sun.jdi.event.VMDisconnectEvent;
+import com.sun.jdi.request.ClassPrepareRequest;
+import com.sun.jdi.request.EventRequest;
+import com.sun.jdi.request.EventRequestManager;
+import com.sun.jdi.request.ModificationWatchpointRequest;
+
+public class FieldMonitor {
+
+ public static final String CLASS_NAME = "TestPostFieldModification";
+ public static final String FIELD_NAME = "value";
+ public static final String ARGUMENTS = "-Xshare:off -XX:+PrintGC";
+
+ public static void main(String[] args)
+ throws IOException, InterruptedException {
+
+ StringBuffer sb = new StringBuffer();
+
+ for (int i=0; i < args.length; i++) {
+ sb.append(' ');
+ sb.append(args[i]);
+ }
+ //VirtualMachine vm = launchTarget(sb.toString());
+ VirtualMachine vm = launchTarget(CLASS_NAME);
+
+ System.out.println("Vm launched");
+ // set watch field on already loaded classes
+ List<ReferenceType> referenceTypes = vm
+ .classesByName(CLASS_NAME);
+ for (ReferenceType refType : referenceTypes) {
+ addFieldWatch(vm, refType);
+ }
+ // watch for loaded classes
+ addClassWatch(vm);
+
+ // process events
+ EventQueue eventQueue = vm.eventQueue();
+ // resume the vm
+
+ Process process = vm.process();
+
+
+ // Copy target's output and error to our output and error.
+ Thread outThread = new StreamRedirectThread("out reader", process.getInputStream());
+ Thread errThread = new StreamRedirectThread("error reader", process.getErrorStream());
+
+ errThread.start();
+ outThread.start();
+
+
+ vm.resume();
+ boolean connected = true;
+ while (connected) {
+ EventSet eventSet = eventQueue.remove();
+ for (Event event : eventSet) {
+ if (event instanceof VMDeathEvent
+ || event instanceof VMDisconnectEvent) {
+ // exit
+ connected = false;
+ } else if (event instanceof ClassPrepareEvent) {
+ // watch field on loaded class
+ System.out.println("ClassPrepareEvent");
+ ClassPrepareEvent classPrepEvent = (ClassPrepareEvent) event;
+ ReferenceType refType = classPrepEvent
+ .referenceType();
+ addFieldWatch(vm, refType);
+ } else if (event instanceof ModificationWatchpointEvent) {
+ System.out.println("sleep for 500 ms");
+ Thread.sleep(500);
+ System.out.println("resume...");
+
+ ModificationWatchpointEvent modEvent = (ModificationWatchpointEvent) event;
+ System.out.println("old="
+ + modEvent.valueCurrent());
+ System.out.println("new=" + modEvent.valueToBe());
+ System.out.println();
+ }
+ }
+ eventSet.resume();
+ }
+ // Shutdown begins when event thread terminates
+ try {
+ errThread.join(); // Make sure output is forwarded
+ outThread.join();
+ } catch (InterruptedException exc) {
+ // we don't interrupt
+ }
+ }
+
+ /**
+ * Find a com.sun.jdi.CommandLineLaunch connector
+ */
+ static LaunchingConnector findLaunchingConnector() {
+ List <Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();
+ Iterator <Connector> iter = connectors.iterator();
+ while (iter.hasNext()) {
+ Connector connector = iter.next();
+ if (connector.name().equals("com.sun.jdi.CommandLineLaunch")) {
+ return (LaunchingConnector)connector;
+ }
+ }
+ throw new Error("No launching connector");
+ }
+ /**
+ * Return the launching connector's arguments.
+ */
+ static Map <String,Connector.Argument> connectorArguments(LaunchingConnector connector, String mainArgs) {
+ Map<String,Connector.Argument> arguments = connector.defaultArguments();
+ for (String key : arguments.keySet()) {
+ System.out.println(key);
+ }
+
+ Connector.Argument mainArg = (Connector.Argument)arguments.get("main");
+ if (mainArg == null) {
+ throw new Error("Bad launching connector");
+ }
+ mainArg.setValue(mainArgs);
+
+ Connector.Argument optionsArg = (Connector.Argument)arguments.get("options");
+ if (optionsArg == null) {
+ throw new Error("Bad launching connector");
+ }
+ optionsArg.setValue(ARGUMENTS);
+ return arguments;
+ }
+
+ static VirtualMachine launchTarget(String mainArgs) {
+ LaunchingConnector connector = findLaunchingConnector();
+ Map arguments = connectorArguments(connector, mainArgs);
+ try {
+ return (VirtualMachine) connector.launch(arguments);
+ } catch (IOException exc) {
+ throw new Error("Unable to launch target VM: " + exc);
+ } catch (IllegalConnectorArgumentsException exc) {
+ throw new Error("Internal error: " + exc);
+ } catch (VMStartException exc) {
+ throw new Error("Target VM failed to initialize: " +
+ exc.getMessage());
+ }
+}
+
+
+ private static void addClassWatch(VirtualMachine vm) {
+ EventRequestManager erm = vm.eventRequestManager();
+ ClassPrepareRequest classPrepareRequest = erm
+ .createClassPrepareRequest();
+ classPrepareRequest.addClassFilter(CLASS_NAME);
+ classPrepareRequest.setEnabled(true);
+ }
+
+
+ private static void addFieldWatch(VirtualMachine vm,
+ ReferenceType refType) {
+ EventRequestManager erm = vm.eventRequestManager();
+ Field field = refType.fieldByName(FIELD_NAME);
+ ModificationWatchpointRequest modificationWatchpointRequest = erm
+ .createModificationWatchpointRequest(field);
+ modificationWatchpointRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
+ modificationWatchpointRequest.setEnabled(true);
+ }
+}
+
+class StreamRedirectThread extends Thread {
+
+ private final BufferedReader in;
+
+ private static final int BUFFER_SIZE = 2048;
+
+ /**
+ * Set up for copy.
+ * @param name Name of the thread
+ * @param in Stream to copy from
+ * @param out Stream to copy to
+ */
+ StreamRedirectThread(String name, InputStream in) {
+ super(name);
+ this.in = new BufferedReader(new InputStreamReader(in));
+ }
+
+ /**
+ * Copy.
+ */
+ public void run() {
+ try {
+ String line;
+ while ((line = in.readLine ()) != null) {
+ System.out.println ("testvm: " + line);
+ }
+ System.out.flush();
+ } catch(IOException exc) {
+ System.err.println("Child I/O Transfer - " + exc);
+ }
+ }
+}