374 lines
12 KiB
Java
374 lines
12 KiB
Java
package net.educoder.debugger;
|
|
|
|
|
|
import com.microsoft.java.debug.core.DebugEvent;
|
|
import com.microsoft.java.debug.core.DebugUtility;
|
|
import com.microsoft.java.debug.core.IBreakpoint;
|
|
import com.microsoft.java.debug.core.IDebugSession;
|
|
import com.sun.jdi.*;
|
|
import com.sun.jdi.event.BreakpointEvent;
|
|
import com.sun.jdi.event.Event;
|
|
import com.sun.jdi.event.StepEvent;
|
|
import com.sun.jdi.request.StepRequest;
|
|
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.*;
|
|
|
|
|
|
/**
|
|
* @Author: youys
|
|
* @Date: 2023/4/6
|
|
* @Description:
|
|
*/
|
|
public class Debugger {
|
|
|
|
private static final String SERIALIZER_CLASS = "__Serializer__";
|
|
private static final String SERIALIZE_DYNAMIC_RESOLVE = "serializeDynamicResolve";
|
|
private Map<String, IBreakpoint> breakpoints = new HashMap<>();
|
|
private static final String OBJECT_TO_CLASS = "objectToString";
|
|
private static IDebugSession debugSession;
|
|
private DebugEvent lastDebugEventFromVM;
|
|
private String inputFilePath = "";
|
|
private String outputFilePath = "";
|
|
private StepRequest stepRequest;
|
|
private Boolean isRunning;
|
|
|
|
public Debugger(String paramString1, String paramString2) {
|
|
debugSession = getDebugSession(paramString1, paramString2);
|
|
this.isRunning = Boolean.valueOf(true);
|
|
|
|
debugSession.getEventHub().events().subscribe(paramDebugEvent -> {
|
|
System.out.println("Event - " + paramDebugEvent.event.getClass().getName());
|
|
Event event = paramDebugEvent.event;
|
|
synchronized (debugSession) {
|
|
if (event instanceof BreakpointEvent || event instanceof StepEvent || event instanceof com.sun.jdi.event.VMDeathEvent || event instanceof com.sun.jdi.event.VMDisconnectEvent) {
|
|
paramDebugEvent.shouldResume = false;
|
|
}
|
|
if (event instanceof com.sun.jdi.event.VMDisconnectEvent || event instanceof com.sun.jdi.event.VMDeathEvent) {
|
|
this.isRunning = Boolean.valueOf(false);
|
|
}
|
|
this.lastDebugEventFromVM = paramDebugEvent;
|
|
if (!paramDebugEvent.shouldResume) {
|
|
debugSession.notifyAll();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
protected static IDebugSession getDebugSession(String paramString1, String paramString2) {
|
|
try {
|
|
VirtualMachineManager virtualMachineManager = Bootstrap.virtualMachineManager();
|
|
debugSession = DebugUtility.launch(virtualMachineManager, paramString1, "", "--enable-preview", null, paramString2, "", null);
|
|
debugSession.getEventHub().events().subscribe(paramDebugEvent -> {
|
|
if (paramDebugEvent.event instanceof com.sun.jdi.event.VMDisconnectEvent) {
|
|
try {
|
|
// debugSession.getEventHub().close();
|
|
} catch (Exception exception) {
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
return debugSession;
|
|
} catch (Exception exception) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private ThreadReference getThreadFromLastEvent() throws Exception {
|
|
ThreadReference threadReference;
|
|
Event event = this.lastDebugEventFromVM.event;
|
|
|
|
if (event instanceof BreakpointEvent) {
|
|
threadReference = ((BreakpointEvent) event).thread();
|
|
} else if (event instanceof StepEvent) {
|
|
threadReference = ((StepEvent) event).thread();
|
|
} else {
|
|
throw new Exception("Unknown type of event - " + event.toString());
|
|
}
|
|
return threadReference;
|
|
}
|
|
|
|
private void writeToProcessStdin(String paramString) throws IOException {
|
|
Process process = debugSession.process();
|
|
OutputStream outputStream = process.getOutputStream();
|
|
outputStream.write(paramString.getBytes());
|
|
outputStream.close();
|
|
}
|
|
|
|
public boolean isRunning() {
|
|
return this.isRunning.booleanValue();
|
|
}
|
|
|
|
public void startProgram() throws Exception {
|
|
Process process = debugSession.process();
|
|
|
|
if (this.inputFilePath != "") {
|
|
Path path = Paths.get(this.inputFilePath, new String[0]);
|
|
String str = new String(Files.readAllBytes(path));
|
|
|
|
OutputStream outputStream = process.getOutputStream();
|
|
outputStream.write(str.getBytes());
|
|
outputStream.close();
|
|
}
|
|
|
|
if (this.outputFilePath != "") {
|
|
StreamGobbler streamGobbler1 = new StreamGobbler(process.getErrorStream(), this.outputFilePath);
|
|
StreamGobbler streamGobbler2 = new StreamGobbler(process.getInputStream(), this.outputFilePath);
|
|
streamGobbler2.start();
|
|
streamGobbler1.start();
|
|
}
|
|
|
|
debugSession.start();
|
|
}
|
|
|
|
public void setBreakPoint(String className, Integer lineNo) throws Exception {
|
|
IBreakpoint iBreakpoint = debugSession.createBreakpoint(className, lineNo.intValue(), 0, null, null);
|
|
iBreakpoint.install();
|
|
this.breakpoints.put(className + ":" + lineNo, iBreakpoint);
|
|
}
|
|
|
|
public void removeBreakPoint(String className, Integer lineNo) throws Exception {
|
|
String str = className + ":" + lineNo;
|
|
IBreakpoint iBreakpoint = this.breakpoints.get(str);
|
|
iBreakpoint.close();
|
|
this.breakpoints.remove(str);
|
|
}
|
|
|
|
public ArrayList<ArrayList<String>> getBreakpoints() {
|
|
ArrayList<ArrayList<String>> arrayList = new ArrayList();
|
|
Set<String> set = this.breakpoints.keySet();
|
|
Iterator<String> iterator = set.iterator();
|
|
while (iterator.hasNext()) {
|
|
String str1 = iterator.next();
|
|
String[] arrayOfString = str1.split(":");
|
|
String str2 = arrayOfString[0];
|
|
String str3 = arrayOfString[1];
|
|
arrayList.add(new ArrayList(Arrays.asList((Object[]) new String[]{str2, str3})));
|
|
}
|
|
return arrayList;
|
|
}
|
|
|
|
public void runContinue() throws Exception {
|
|
cleanPreviousStepRequest();
|
|
ThreadReference threadReference = getThreadFromLastEvent();
|
|
resetLastDebugEvent();
|
|
threadReference.resume();
|
|
}
|
|
|
|
private void cleanPreviousStepRequest() throws Exception {
|
|
if (this.stepRequest != null) {
|
|
this.stepRequest.disable();
|
|
}
|
|
}
|
|
|
|
public void runStepInto() throws Exception {
|
|
cleanPreviousStepRequest();
|
|
ThreadReference threadReference = getThreadFromLastEvent();
|
|
this.stepRequest = DebugUtility.createStepIntoRequest(threadReference, new String[0]);
|
|
this.stepRequest.enable();
|
|
resetLastDebugEvent();
|
|
threadReference.resume();
|
|
}
|
|
|
|
public void runStepOver() throws Exception {
|
|
cleanPreviousStepRequest();
|
|
ThreadReference threadReference = getThreadFromLastEvent();
|
|
this.stepRequest = DebugUtility.createStepOverRequest(threadReference, new String[0]);
|
|
this.stepRequest.enable();
|
|
resetLastDebugEvent();
|
|
threadReference.resume();
|
|
}
|
|
|
|
public void runStepOut() throws Exception {
|
|
cleanPreviousStepRequest();
|
|
ThreadReference threadReference = getThreadFromLastEvent();
|
|
this.stepRequest = DebugUtility.createStepOutRequest(threadReference, new String[0]);
|
|
this.stepRequest.enable();
|
|
resetLastDebugEvent();
|
|
threadReference.resume();
|
|
}
|
|
|
|
private void resetLastDebugEvent() {
|
|
this.lastDebugEventFromVM = null;
|
|
}
|
|
|
|
public void waitForVMPause() throws InterruptedException {
|
|
while (true) {
|
|
synchronized (debugSession) {
|
|
if (!isRunning()) {
|
|
return;
|
|
}
|
|
|
|
if (this.lastDebugEventFromVM != null && !this.lastDebugEventFromVM.shouldResume) {
|
|
return;
|
|
}
|
|
|
|
debugSession.wait(1L);
|
|
}
|
|
}
|
|
}
|
|
|
|
private String callSerializerMethod(String paramString, List<Value> paramList) throws Exception {
|
|
VirtualMachine virtualMachine = debugSession.getVM();
|
|
List<ReferenceType> list = virtualMachine.classesByName(SERIALIZER_CLASS);
|
|
|
|
ClassType classType = (ClassType) list.get(0);
|
|
|
|
List<Method> list1 = classType.methodsByName(paramString);
|
|
Method method = list1.get(0);
|
|
|
|
ThreadReference threadReference = getThreadFromLastEvent();
|
|
|
|
|
|
Value value = classType.invokeMethod(threadReference, method, paramList, 1);
|
|
String str = value.toString();
|
|
|
|
str = str.substring(1, str.length() - 1);
|
|
return str;
|
|
}
|
|
|
|
private String serializeDynamicResolve(Value paramValue) {
|
|
try {
|
|
List<Value> list = Arrays.asList(new Value[]{paramValue});
|
|
return callSerializerMethod(SERIALIZE_DYNAMIC_RESOLVE, list);
|
|
} catch (Exception exception) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private String objectToString(Value paramValue) {
|
|
try {
|
|
List<Value> list = Arrays.asList(new Value[]{paramValue});
|
|
return callSerializerMethod(OBJECT_TO_CLASS, list);
|
|
} catch (Exception exception) {
|
|
|
|
try {
|
|
return paramValue.toString();
|
|
} catch (Exception exception1) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private String serialiseVariableValue(Value paramValue) {
|
|
String str = "null";
|
|
if (paramValue == null) {
|
|
return str;
|
|
}
|
|
|
|
str = serializeDynamicResolve(paramValue);
|
|
if (str != null) {
|
|
return str;
|
|
}
|
|
|
|
str = objectToString(paramValue);
|
|
if (str != null) {
|
|
return str;
|
|
}
|
|
|
|
return "Unable to serialise this object.";
|
|
}
|
|
|
|
private ArrayList<Variable> getLocalVariablesWithException() throws Exception {
|
|
ArrayList<Variable> arrayList = new ArrayList();
|
|
ThreadReference threadReference = getThreadFromLastEvent();
|
|
StackFrame stackFrame = threadReference.frame(0);
|
|
if (stackFrame.location().method().isNative()) {
|
|
return arrayList;
|
|
}
|
|
|
|
try {
|
|
for (LocalVariable localVariable : stackFrame.visibleVariables()) {
|
|
|
|
stackFrame = threadReference.frame(0);
|
|
String str1 = localVariable.name();
|
|
String str2 = serialiseVariableValue(stackFrame.getValue(localVariable));
|
|
Variable variable = new Variable(str1, str2);
|
|
arrayList.add(variable);
|
|
}
|
|
} catch (AbsentInformationException absentInformationException) {
|
|
absentInformationException.printStackTrace();
|
|
|
|
List<Value> list = stackFrame.getArgumentValues();
|
|
if (list == null) {
|
|
return arrayList;
|
|
}
|
|
|
|
byte b = 0;
|
|
for (Value value : list) {
|
|
Variable variable = new Variable("arg" + b, serialiseVariableValue(value));
|
|
arrayList.add(variable);
|
|
b++;
|
|
}
|
|
|
|
return arrayList;
|
|
}
|
|
|
|
return arrayList;
|
|
}
|
|
|
|
public ArrayList<Variable> getLocalVariables() {
|
|
try {
|
|
return getLocalVariablesWithException();
|
|
} catch (Exception exception) {
|
|
exception.printStackTrace();
|
|
return new ArrayList<>();
|
|
}
|
|
}
|
|
|
|
private Location getLocation() throws Exception {
|
|
ThreadReference threadReference = getThreadFromLastEvent();
|
|
StackFrame stackFrame = threadReference.frame(0);
|
|
return stackFrame.location();
|
|
}
|
|
|
|
public String getLocationFilename() {
|
|
try {
|
|
Location location = getLocation();
|
|
return location.sourceName();
|
|
} catch (Exception exception) {
|
|
exception.printStackTrace();
|
|
|
|
return "";
|
|
}
|
|
}
|
|
|
|
public Integer getLocationLineNumber() {
|
|
try {
|
|
Location location = getLocation();
|
|
return Integer.valueOf(location.lineNumber());
|
|
} catch (Exception exception) {
|
|
exception.printStackTrace();
|
|
|
|
return Integer.valueOf(-1);
|
|
}
|
|
}
|
|
|
|
public String getStatus() {
|
|
try {
|
|
ThreadReference threadReference = getThreadFromLastEvent();
|
|
if (threadReference.frameCount() > 0) {
|
|
return "PAUSED";
|
|
}else{
|
|
return "RUNNING";
|
|
}
|
|
} catch (Exception exception) {
|
|
return "STOP";
|
|
}
|
|
}
|
|
|
|
public void setInputFile(String paramString) {
|
|
this.inputFilePath = paramString;
|
|
}
|
|
|
|
public void setOutputFile(String paramString) {
|
|
this.outputFilePath = paramString;
|
|
}
|
|
}
|