package st.gravel.support.jvm.debugger;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.ProcessBuilder.Redirect;
import java.util.List;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.Method;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.event.BreakpointEvent;
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.VMDeathEvent;
import com.sun.jdi.event.VMDisconnectEvent;
import com.sun.jdi.request.BreakpointRequest;
import com.sun.jdi.request.ClassPrepareRequest;
public class VMTargetStarter {
private static int nextPort = 8000;
private VMTargetStarter(int debugPort) {
super();
this.debugPort = debugPort;
}
private final int debugPort;
public Process startSecondJVM(Class<?> mainClassToStart) throws IOException {
String separator = System.getProperty("file.separator");
String classpath = System.getProperty("java.class.path");
String path = System.getProperty("java.home") + separator + "bin"
+ separator + "java";
ProcessBuilder processBuilder = new ProcessBuilder(path, "-Xdebug",
"-Xrunjdwp:transport=dt_socket,address=" + debugPort
+ ",server=y,suspend=y", "-cp", classpath,
mainClassToStart.getCanonicalName());
processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
final Process process = processBuilder.start();
Thread closeChildThread = new Thread() {
public void run() {
process.destroy();
}
};
Runtime.getRuntime().addShutdownHook(closeChildThread);
return process;
}
public static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
public VMRemoteTarget createJVM() throws IOException, InterruptedException,
IncompatibleThreadStateException {
Process process = startSecondJVM(VMLocalTarget.class);
sleep(90);
// connect
VirtualMachine vm = new VMAcquirer().connect(debugPort);
ClassPrepareRequest createClassPrepareRequest = vm
.eventRequestManager().createClassPrepareRequest();
createClassPrepareRequest.addClassFilter(VMLocalTarget.class.getName());
createClassPrepareRequest.enable();
vm.resume();
List<ThreadReference> allThreads = vm.allThreads();
for (ThreadReference threadReference : allThreads) {
System.out.println(threadReference+" isSuspended: "+threadReference.isSuspended()+" suspendCount: "+threadReference.suspendCount());
}
// process events
EventQueue eventQueue = vm.eventQueue();
while (true) {
EventSet eventSet = eventQueue.remove();
for (Event event : eventSet) {
if (event instanceof ClassPrepareEvent) {
event.request().disable();
installHaltPoint(vm);
}
if (event instanceof VMDeathEvent
|| event instanceof VMDisconnectEvent) {
return null;
}
if (event instanceof BreakpointEvent) {
event.request().disable();
ThreadReference thread = ((BreakpointEvent) event).thread();
return new VMRemoteTarget(process, vm, thread, debugPort);
}
}
eventSet.resume();
}
}
private void installHaltPoint(VirtualMachine vm) {
List<ReferenceType> targetClasses = vm
.classesByName(VMLocalTarget.class.getName());
ReferenceType classRef = targetClasses.get(0);
Method meth = classRef.methodsByName("haltPoint").get(0);
BreakpointRequest req = vm.eventRequestManager()
.createBreakpointRequest(meth.location());
req.setSuspendPolicy(BreakpointRequest.SUSPEND_EVENT_THREAD);
req.enable();
}
public static VMRemoteTarget newRemote(int port) {
try {
return new VMTargetStarter(port).createJVM();
} catch (IOException | InterruptedException
| IncompatibleThreadStateException e) {
throw new RuntimeException(e);
}
}
public static VMRemoteTarget newRemote() {
return newRemote(nextPort++);
}
}