Compare commits

...

7 Commits
master ... dev

Author SHA1 Message Date
youys dcdab43146 fix(Debugger) list,map,对象类型的变量展示值 2023-08-24 10:09:58 +08:00
youys 7b0d778e82 fix(DebuggerController) 代码编译问题 2023-08-17 15:52:07 +08:00
youys 5f0cae3f06 fix(StreamGobbler) 输出写文件追加 2023-08-17 10:53:35 +08:00
youys 4f63295ed7 jar 2023-07-13 17:37:47 +08:00
youys 5ef18d2173 jar 2023-07-13 17:37:05 +08:00
youys c19f49d38a java在线调试 2023-07-11 16:31:04 +08:00
youys 259ec8fe89 接口调整 2023-07-07 17:35:05 +08:00
11 changed files with 404 additions and 41 deletions

1
.gitignore vendored
View File

@ -8,6 +8,5 @@ target/
.settings/
*.log
bin/
lib/
src/main/java/test/
.vscode/

BIN
lib/minimal-json.jar Normal file

Binary file not shown.

BIN
lib/precompiled.jar Normal file

Binary file not shown.

13
pom.xml
View File

@ -82,6 +82,19 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<!-- <release>17</release>-->
<compilerArgs>
<arg>--add-exports</arg>
<arg>jdk.jdi/com.sun.tools.jdi=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -10,6 +10,7 @@ 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 com.sun.tools.jdi.ClassObjectReferenceImpl;
import java.io.IOException;
import java.io.OutputStream;
@ -36,9 +37,11 @@ public class Debugger {
private String outputFilePath = "";
private StepRequest stepRequest;
private Boolean isRunning;
private String mainClass;
public Debugger(String paramString1, String paramString2) {
debugSession = getDebugSession(paramString1, paramString2);
public Debugger(String mainClass, String classPath) {
this.mainClass = mainClass;
debugSession = getDebugSession(mainClass, classPath);
this.isRunning = Boolean.valueOf(true);
debugSession.getEventHub().events().subscribe(paramDebugEvent -> {
@ -67,7 +70,7 @@ public class Debugger {
debugSession.getEventHub().events().subscribe(paramDebugEvent -> {
if (paramDebugEvent.event instanceof com.sun.jdi.event.VMDisconnectEvent) {
try {
// debugSession.getEventHub().close();
debugSession.getEventHub().close();
} catch (Exception exception) {
}
}
@ -140,18 +143,26 @@ public class Debugger {
this.breakpoints.remove(str);
}
public ArrayList<ArrayList<String>> getBreakpoints() {
ArrayList<ArrayList<String>> arrayList = new ArrayList();
public Map<String, List<Integer>> getBreakpoints() {
Map<String, List<Integer>> listMap = new HashMap<>();
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})));
String className = arrayOfString[0];
Integer lineNo = Integer.valueOf(arrayOfString[1]);
if (listMap.containsKey(className)) {
List<Integer> lineNoList = listMap.get(className);
lineNoList.add(lineNo);
} else {
List<Integer> lineNoList = new ArrayList<>();
lineNoList.add(lineNo);
listMap.put(className, lineNoList);
}
}
return arrayList;
return listMap;
}
public void runContinue() throws Exception {
@ -218,6 +229,22 @@ public class Debugger {
VirtualMachine virtualMachine = debugSession.getVM();
List<ReferenceType> list = virtualMachine.classesByName(SERIALIZER_CLASS);
if (list.size() == 0) {
ClassLoaderReference reference = virtualMachine.classesByName(mainClass).get(0).classLoader();
Method method = virtualMachine.classesByName("jdk.internal.loader.ClassLoaders$AppClassLoader").get(0).methodsByName("loadClass").get(1);
List<Value> arguments = new ArrayList<>();
arguments.add(virtualMachine.mirrorOf(SERIALIZER_CLASS));
ThreadReference threadReference = getThreadFromLastEvent();
Value value = reference.invokeMethod(threadReference, method, arguments, 1);
ReferenceType referenceType = ((ClassObjectReferenceImpl) value).referenceType();
ClassType classType = (ClassType) referenceType;
method = referenceType.methodsByName("forName").get(0);
classType.invokeMethod(threadReference, method, arguments, 1);
}
list = virtualMachine.classesByName(SERIALIZER_CLASS);
ClassType classType = (ClassType) list.get(0);
List<Method> list1 = classType.methodsByName(paramString);
@ -238,6 +265,7 @@ public class Debugger {
List<Value> list = Arrays.asList(new Value[]{paramValue});
return callSerializerMethod(SERIALIZE_DYNAMIC_RESOLVE, list);
} catch (Exception exception) {
exception.printStackTrace();
return null;
}
}
@ -247,6 +275,7 @@ public class Debugger {
List<Value> list = Arrays.asList(new Value[]{paramValue});
return callSerializerMethod(OBJECT_TO_CLASS, list);
} catch (Exception exception) {
exception.printStackTrace();
try {
return paramValue.toString();
@ -355,7 +384,7 @@ public class Debugger {
ThreadReference threadReference = getThreadFromLastEvent();
if (threadReference.frameCount() > 0) {
return "PAUSED";
}else{
} else {
return "RUNNING";
}
} catch (Exception exception) {

View File

@ -1,11 +1,20 @@
package net.educoder.debugger;
import com.google.gson.Gson;
import com.microsoft.java.debug.core.DebugUtility;
import net.educoder.model.DebugResult;
import net.educoder.model.ShellResult;
import net.educoder.util.ShellUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@ -19,11 +28,14 @@ import java.util.Map;
@RestController
public class DebuggerController {
public static final String CLASS_PATHS = "/Users/youyongsheng/Desktop/ZQ/online-debug/evaluation/test-java/part4/src:/Users/youyongsheng/Desktop/ZQ/online-debug/evaluation/test-java/part4/src/precompiled.jar";
public static final String MAIN_CLASS = "__Driver__";
private static final Debugger debugger = new Debugger(MAIN_CLASS, CLASS_PATHS);
private static Debugger debugger = null;
private static final Gson gson = new Gson();
private String outputPath;
private static final String workspace = "/data/workspace";
private final Logger logger = LoggerFactory.getLogger(DebuggerController.class);
@GetMapping("/")
public String home() {
@ -32,17 +44,46 @@ public class DebuggerController {
/**
* 启动程序
*
* @return
* @throws Exception
*/
@GetMapping("/start-program")
public String startProgram() throws Exception {
debugger.startProgram();
return "OK";
@PostMapping("/start-program")
public String startProgram(@RequestParam("classPath") String classPath,
@RequestParam("mainClass") String mainClass,
@RequestParam("breakPoints") String breakPoints,
@RequestParam("fileInputPath") String fileInputPath,
@RequestParam("fileOutPath") String fileOutPath,
@RequestParam("originExecuteFile") String originExecuteFile) throws Exception {
try {
// 编译代码
compileCode(originExecuteFile);
initDebuggerInstance(mainClass, classPath);
outputPath = fileOutPath;
debugger.setInputFile(fileInputPath);
debugger.setOutputFile(fileOutPath);
for (String breakPoint : breakPoints.split(",")) {
String[] segmentation = breakPoint.split(":");
debugger.setBreakPoint(segmentation[0], Integer.valueOf(segmentation[1]));
}
debugger.startProgram();
return getCurrentState();
}catch (RuntimeException e){
String errorMessage = e.getMessage();
DebugResult result = new DebugResult();
result.setIs_over(true);
result.setCurrent_line(-1);
result.setExpressions(new HashMap<>());
result.setLocals(new HashMap<>());
result.setOut(errorMessage);
return gson.toJson(result);
}
}
/**
* 设置输入
*
* @param filePath
* @return
*/
@ -54,19 +95,22 @@ public class DebuggerController {
/**
* 设置输出
*
* @param filePath
* @return
*/
@GetMapping("/set-output-file")
public String setOutputFile(@RequestParam("filepath") String filePath) {
debugger.setOutputFile(filePath);
outputPath = filePath;
return "OK";
}
/**
* 设置断点
* @param className
* @param lineNo
*
* @param className Debug.java -> Debug
* @param lineNo 10
* @return
* @throws Exception
*/
@ -74,11 +118,12 @@ public class DebuggerController {
public String setBreakpoint(@RequestParam("className") String className, @RequestParam("lineNo") Integer lineNo)
throws Exception {
debugger.setBreakPoint(className, lineNo);
return "OK";
return getCurrentState();
}
/**
* 移除断点
*
* @param className
* @param lineNo
* @return
@ -88,21 +133,22 @@ public class DebuggerController {
public String removeBreakpoint(@RequestParam("className") String className, @RequestParam("lineNo") Integer lineNo)
throws Exception {
debugger.removeBreakPoint(className, lineNo);
return "OK";
return getCurrentState();
}
/**
* 获取断点
*
* @return
*/
@GetMapping("/get-breakpoints")
public String getBreakpoints() {
ArrayList<ArrayList<String>> breakpoints = debugger.getBreakpoints();
return gson.toJson(breakpoints);
return gson.toJson(debugger.getBreakpoints());
}
/**
* 跳过断点下一个
*
* @return
* @throws Exception
*/
@ -110,11 +156,12 @@ public class DebuggerController {
public String runContinue() throws Exception {
debugger.waitForVMPause();
debugger.runContinue();
return "OK";
return getCurrentState();
}
/**
* 单步调试
*
* @return
* @throws Exception
*/
@ -122,11 +169,12 @@ public class DebuggerController {
public String runStepInto() throws Exception {
debugger.waitForVMPause();
debugger.runStepInto();
return "OK";
return getCurrentState();
}
/**
* 单步跳出
*
* @return
* @throws Exception
*/
@ -134,11 +182,12 @@ public class DebuggerController {
public String runStepOut() throws Exception {
debugger.waitForVMPause();
debugger.runStepOut();
return "OK";
return getCurrentState();
}
/**
* 单步跳过下一步
*
* @return
* @throws Exception
*/
@ -146,12 +195,13 @@ public class DebuggerController {
public String runStepOver() throws Exception {
debugger.waitForVMPause();
debugger.runStepOver();
return "OK";
return getCurrentState();
}
/**
* 获取当前状态
*
* @return
* @throws Exception
*/
@ -159,18 +209,60 @@ public class DebuggerController {
public String getCurrentState() throws Exception {
debugger.waitForVMPause();
Map<String, Object> map = new HashMap<>(2);
DebugResult result = new DebugResult();
if (!debugger.isRunning()) {
return gson.toJson(map);
result.setExpressions(new HashMap<>());
result.setLocals(new HashMap<>());
result.setOut(FileUtils.readFileToString(new File(outputPath), Charset.defaultCharset()));
return gson.toJson(result);
}
ArrayList<Variable> localVariables = debugger.getLocalVariables();
String locationFilename = debugger.getLocationFilename();
Integer locationLineNumber = debugger.getLocationLineNumber();
result.setExpressions(new HashMap<>());
result.setFilename(debugger.getLocationFilename());
result.setCurrent_line(debugger.getLocationLineNumber());
map.put("location", locationFilename + ":" + locationLineNumber);
map.put("variables", localVariables);
map.put("status", debugger.getStatus());
return gson.toJson(map);
ArrayList<Variable> localVariables = debugger.getLocalVariables();
if (localVariables != null) {
Map<String, String> locals = new HashMap<>();
for (Variable localVariable : localVariables) {
locals.put(localVariable.name, localVariable.value);
}
result.setLocals(locals);
}
result.setBreakpoints(debugger.getBreakpoints());
result.setOut(FileUtils.readFileToString(new File(outputPath), Charset.defaultCharset()));
return gson.toJson(result);
}
/**
* 初始化
*
* @param mainClass 执行类
* @param classPath class path 路径
*/
private static void initDebuggerInstance(String mainClass, String classPath) {
if (debugger == null) {
synchronized (DebuggerController.class) {
if (debugger == null) {
debugger = new Debugger(mainClass, classPath);
}
}
}
}
/**
* 编译代码
*
* @param originalEvaluateFile 原始评测文件
*/
private void compileCode(String originalEvaluateFile) {
String compilePath = workspace + File.separator + originalEvaluateFile.substring(0, originalEvaluateFile.lastIndexOf("/") + 1);
String compileCommand = StringUtils.join("javac -g -cp ", compilePath,":", workspace,"/precompiled.jar:", workspace,"/minimal-json.jar ", " ", compilePath+"*.java");
ShellResult shellResult = ShellUtil.executeAndGetExitStatus(compileCommand);
logger.info("编译代码command:{}, status:{}, out:{}", compileCommand, shellResult.getExitStatus(), shellResult.getOut());
if(shellResult.getExitStatus() != 0){
throw new RuntimeException(shellResult.getOut().replaceAll(workspace + "/", ""));
}
}
}

View File

@ -22,11 +22,10 @@ public class StreamGobbler extends Thread {
InputStreamReader inputStreamReader = new InputStreamReader(this.inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
FileWriter fileWriter = new FileWriter(this.filepath);
FileWriter fileWriter = new FileWriter(this.filepath, true);
int i;
while ((i = bufferedReader.read()) != -1) {
System.out.println(filepath+ " write---------"+(char)i);
fileWriter.write((char)i);
fileWriter.flush();
}

View File

@ -0,0 +1,140 @@
package net.educoder.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
import java.util.Map;
/**
* @Author: youys
* @Date: 2023/7/7
* @Description:
*/
public class DebugResult {
/**
* 当前行
*/
private int current_line = -1;
/**
* 当前文件
*/
private String filename = "";
/**
* 调试唯一标识
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private String unique;
/**
* 断点信息
*/
private Map<String, List<Integer>> breakpoints;
/**
* 表达式
*/
private Map<String, String> expressions;
/**
* 本地变量
*/
private Map<String, String> locals;
/**
* 输出
*/
private String out;
/**
* 是否结束 true 已结束 false 调试中
*/
private boolean is_over;
/**
* 是否需要跳转文件 true 跳转 false 不跳转
*/
private boolean is_jump;
public int getCurrent_line() {
return current_line;
}
public void setCurrent_line(int current_line) {
this.current_line = current_line;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public boolean isIs_over() {
return is_over;
}
public void setIs_over(boolean is_over) {
this.is_over = is_over;
}
public String getUnique() {
return unique;
}
public void setUnique(String unique) {
this.unique = unique;
}
public Map<String, List<Integer>> getBreakpoints() {
return breakpoints;
}
public void setBreakpoints(Map<String, List<Integer>> breakpoints) {
this.breakpoints = breakpoints;
}
public Map<String, String> getExpressions() {
return expressions;
}
public void setExpressions(Map<String, String> expressions) {
unicodeToCn(expressions);
this.expressions = expressions;
}
public Map<String, String> getLocals() {
return locals;
}
public void setLocals(Map<String, String> locals) {
unicodeToCn(locals);
this.locals = locals;
}
public String getOut() {
return out;
}
public void setOut(String out) {
this.out = out;
}
public boolean isIs_jump() {
return is_jump;
}
public void setIs_jump(boolean is_jump) {
this.is_jump = is_jump;
}
private void unicodeToCn(Map<String,String> map){
for (Map.Entry<String, String> entry : map.entrySet()) {
// map.put(entry.getKey(), UnicodeUtil.toString(entry.getValue()));
}
}
}

View File

@ -0,0 +1,35 @@
package net.educoder.model;
/**
* shell执行结果
*
* @author 威少
*/
public class ShellResult {
/**
* 退出码
*/
private Integer exitStatus;
/**
* 实际输出
*/
private String out;
public Integer getExitStatus() {
return exitStatus;
}
public void setExitStatus(Integer exitStatus) {
this.exitStatus = exitStatus;
}
public String getOut() {
return out;
}
public void setOut(String out) {
this.out = out;
}
}

View File

@ -0,0 +1,56 @@
package net.educoder.util;
import net.educoder.model.ShellResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public final class ShellUtil {
private static final Logger logger = LoggerFactory.getLogger(ShellUtil.class);
/**
* 执行shell命令并获取输出
*/
public static String execute(String command) {
return executeAndGetExitStatus(command).getOut();
}
/**
* 执行命令并获得输出以及退出码
*/
public static ShellResult executeAndGetExitStatus(String command) {
ShellResult result = new ShellResult();
StringBuilder out = new StringBuilder();
Integer exitStatus = -1;
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", command);
pb.redirectErrorStream(true);
try {
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(System.getProperty("line.separator"));
}
exitStatus = process.waitFor();
} catch (Exception e) {
logger.error("执行shell出错, command:{}", command, e);
}
result.setOut(out.toString().trim());
result.setExitStatus(exitStatus);
logger.debug("execute shell command: {}, out: {}, status: {}", command, out, exitStatus);
return result;
}
}

View File

@ -1,2 +1,2 @@
server:
port: 8083
port: 8080