Files
window-axis-innovators-box/src/main/java/org/tzd/debug/ClassDebug.java
tzdwindows 7 06fe2bedf4 feat(debug): 添加调试窗口和类监控功能
- 新增 DebugWindow 类用于显示调试信息
- 在主程序中添加调试窗口的创建和显示逻辑
- 新增 ClassDebug 类提供类监控和调试功能
- 更新 build.gradle 添加 byte-buddy 依赖
2025-06-27 09:09:55 +08:00

228 lines
9.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package org.tzd.debug;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.api.dog.agent.VirtualMachine;
import org.tzd.debug.util.GlobalObjects;
import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.stream.Collectors;
/**
* 对类进行监控和debug操作
* @author tzdwindows 7
*/
public class ClassDebug {
private static final Logger logger = LogManager.getLogger(ClassDebug.class);
private static Instrumentation instrumentation;
/**
* 监控类加载
* @param monitor 监控器
*/
public static void monitoringLoading(LoadingMonitor monitor) {
if (instrumentation != null) {
instrumentation.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws IllegalClassFormatException {
monitor.load(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
return classfileBuffer;
}
});
} else {
logger.error("Instrumentation is null");
try {
VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true);
instrumentation = vm.getInstrumentation();
} catch (Exception e) {
logger.error("Failed to attach to VM: {}", e.getMessage());
}
monitoringLoading(monitor);
}
}
public static Instrumentation getInstrumentation() {
if (instrumentation == null) {
try {
VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true);
instrumentation = vm.getInstrumentation();
} catch (Exception e) {
logger.error("Failed to attach to VM: {}", e.getMessage());
}
}
return instrumentation;
}
/**
* 执行动态代码
* @param code 要执行的代码
* @return 执行结果(空字符串表示成功,否则为错误信息)
*/
public static String executeCode(String code) {
GlobalObjects globalObjects = new GlobalObjects();
globalObjects.instrumentation = instrumentation;
// 1. 预处理代码替换print/printf为System.out
String processedCode = code.trim();
if (processedCode.startsWith("print(") && processedCode.endsWith(");")) {
processedCode = "System.out.println" + processedCode.substring("print".length());
} else if (processedCode.startsWith("printf(") && processedCode.endsWith(");")) {
processedCode = "System.out.printf" + processedCode.substring("printf".length());
}
// 2. 构建完整类代码
String className = "DynamicExecutedCode";
String fullCode = "package dynamic;\n" + // 添加包声明避免冲突
"import org.tzd.debug.util.GlobalObjects;import java.util.*;\n" +
"public class " + className + " {\n" +
" public static GlobalObjects global;\n" + // 静态字段接收全局对象
" public static void run() {\n" +
" try {\n" + // 添加try-catch捕获用户代码异常
processedCode + "\n" +
" } catch (Throwable t) {\n" +
" throw new RuntimeException(t);\n" + // 包装异常以保持堆栈
" }\n" +
" }\n" +
"}";
Path tempDir = null;
try {
// 3. 创建临时目录和源文件
tempDir = Files.createTempDirectory("dynamicCode");
Path sourceDir = tempDir.resolve("dynamic");
Files.createDirectories(sourceDir);
Path sourceFile = sourceDir.resolve(className + ".java");
Files.write(sourceFile, fullCode.getBytes(StandardCharsets.UTF_8));
// 4. 编译代码
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
return "错误找不到Java编译器。请使用JDK运行此程序。";
}
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
String[] compileOptions = {"-d", tempDir.toString(), "-cp", System.getProperty("java.class.path")};
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(sourceFile);
JavaCompiler.CompilationTask task = compiler.getTask(
null, fileManager, diagnostics,
Arrays.asList(compileOptions), null, compilationUnits
);
boolean success = task.call();
if (!success) {
StringBuilder errorMsg = new StringBuilder("编译错误:\n");
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
errorMsg.append(String.format(
"Line %d: %s\n",
diagnostic.getLineNumber(),
diagnostic.getMessage(Locale.getDefault())
));
}
return errorMsg.toString();
}
}
// 5. 加载并执行
URLClassLoader classLoader = new URLClassLoader(
new URL[]{tempDir.toUri().toURL()},
ClassDebug.class.getClassLoader()
);
Class<?> loadedClass = classLoader.loadClass("dynamic." + className);
// 注入全局对象
Field globalField = loadedClass.getDeclaredField("global");
globalField.set(null, globalObjects);
Method runMethod = loadedClass.getMethod("run");
runMethod.invoke(null);
return "";
} catch (Throwable e) {
// 6. 异常处理
Throwable cause = e;
while (cause.getCause() != null) {
cause = cause.getCause();
}
StringWriter sw = new StringWriter();
cause.printStackTrace(new PrintWriter(sw));
return "运行时错误:" + cause.getMessage() + "\n堆栈跟踪:\n" + sw.toString();
} finally {
// 7. 清理资源
if (tempDir != null) {
try {
Files.walk(tempDir)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (IOException ignored) {}
}
}
}
public static void main(String[] args) {
executeCode("JOptionPane.showMessageDialog(null, \"普通对话框\");");
}
/**
* 获取已加载的类
* @return 已加载的类
*/
public static Class<?>[] getLoadedClasses() {
if (instrumentation == null){
try {
VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true);
instrumentation = vm.getInstrumentation();
} catch (Exception e) {
logger.error("Failed to attach to VM: {}", e.getMessage());
}
}
return instrumentation.getAllLoadedClasses();
}
/**
* 获取类的方法与字段
* @param clazz 类名
* @return 类的方法与字段
*/
public static String getMethodAndField(String clazz) {
try {
Class<?> aClass = Class.forName(clazz);
String methods = Arrays.stream(aClass.getDeclaredMethods())
.map(Method::toString)
.collect(Collectors.joining("\n"));
String fields = Arrays.stream(aClass.getDeclaredFields())
.map(Field::toString)
.collect(Collectors.joining("\n"));
return "方法:\n" + methods + "\n\n字段:\n" + fields;
} catch (ClassNotFoundException e) {
throw new RuntimeException("Class not found: " + clazz, e);
}
}
public interface LoadingMonitor {
void load(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer);
}
}