java-debug/src/main/java/net/educoder/debugger/Debugger.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;
}
}