IDEA 的 debug 怎么實現(xiàn)?出于這個好奇心,我越挖越深!
點擊上方“碼農(nóng)突圍”,馬上關(guān)注 這里是碼農(nóng)充電第一站,回復(fù)“666”,獲取一份專屬大禮包 真愛,請設(shè)置“星標(biāo)”或點個“在看

來源 | https://zhenbianshu.github.io/
對 Debug 的好奇

ASM
動態(tài)生成字節(jié)碼
ASM 框架
ASM Bytecode Outline ,在要查看的類文件里右鍵選擇 Show bytecode Outline 即可以右側(cè)的工具欄查看我們要生成的字節(jié)碼。對照著示例,我們就可以很輕松地寫出操作字節(jié)碼的 Java 代碼了。ASMified 標(biāo)簽欄,我們甚至可以直接獲取到 ASM 的使用代碼。
常用方法
visitMethod()/visitAnnotation() 等方法,用以定義對類結(jié)構(gòu)(如方法、字段、注解)的訪問方法。Instrument
介紹
instrument。使用
ClassFileTransformer 接口定義一個類文件轉(zhuǎn)換器。它唯一的一個 transform() 方法會在類文件被加載時調(diào)用,在 transform 方法里,我們可以對傳入的二進制字節(jié)碼進行改寫或替換,生成新的字節(jié)碼數(shù)組后返回,JVM 會使用 transform 方法返回的字節(jié)碼數(shù)據(jù)進行類的加載。JVM TI
介紹
Agent
-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:3333,而 -agentlib 選項就指定了我們要加載的 Java Agent,jdwp 是 agent 的名字,在 linux 系統(tǒng)中,我們可以在 jre 目錄下找到 jdwp.so 庫文件。jdi->jdwp->jvmti,我們通過 JDI 接口發(fā)送調(diào)試指令,而 jdwp 就相當(dāng)于一個通道,幫我們翻譯 JDI 指令到 JVM TI,最底層的 JVM TI 最終實現(xiàn)對 JVM 的操作。使用
premain() 或 agentmain() 方法來實現(xiàn)。而要實現(xiàn) 1.6 以上的動態(tài) instrument 功能,實現(xiàn) agentmain 方法即可。Instrumentation.retransformClasses() 方法實現(xiàn)對目標(biāo)類的重定義。VirtualMachine 類提供了 attach 一個本地 JVM 的功能,它需要我們傳入一個本地 JVM 的 pid, tools.jar 可以在 jre 目錄下找到。agent生成
MANIFEST.MF 文件的一些參數(shù),允許我們重新定義類。如果你的 agent 實現(xiàn)還需要引用一些其他類庫時,還需要將這些類庫都打包到此 jar 包中,下面是我的 pom 文件配置。<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Agent-Class>asm.TestAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
<Manifest-Version>1.0</Manifest-Version>
<Permissions>all-permissions</Permissions>
</manifestEntries>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
mvn assembly:assembl 命令生成 jar-with-dependencies 作為 agent。代碼實現(xiàn)
被修改的類
public class TransformTarget {
public static void main(String[] args) {
while (true) {
try {
Thread.sleep(3000L);
} catch (Exception e) {
break;
}
printSomething();
}
}
public static void printSomething() {
System.out.println("hello");
}
}
Agent
public class TestAgent {
public static void agentmain(String args, Instrumentation inst) {
inst.addTransformer(new TestTransformer(), true);
try {
inst.retransformClasses(TransformTarget.class);
System.out.println("Agent Load Done.");
} catch (Exception e) {
System.out.println("agent load failed!");
}
}
}
public class TestTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("Transforming " + className);
ClassReader reader = new ClassReader(classfileBuffer);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor classVisitor = new TestClassVisitor(Opcodes.ASM5, classWriter);
reader.accept(classVisitor, ClassReader.SKIP_DEBUG);
return classWriter.toByteArray();
}
class TestClassVisitor extends ClassVisitor implements Opcodes {
TestClassVisitor(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if (name.equals("printSomething")) {
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(19, l0);
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("bytecode replaced!");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLineNumber(20, l1);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(2, 0);
mv.visitEnd();
TransformTarget.printSomething();
}
return mv;
}
}
}
Attacher
public class Attacher {
public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException {
VirtualMachine vm = VirtualMachine.attach("34242"); // 目標(biāo) JVM pid
vm.loadAgent("/path/to/agent.jar");
}
}

小結(jié)
- END - 最近熱文
? 再見 Win10!下一代操作系統(tǒng)要來了! ? 女友回老家了!沒吊事,手把手帶你搭建一臺服務(wù)器! ? 尼瑪,Github上最邪惡的開源項目了!未滿18或者女孩子勿進哦~ ? 永別了,91網(wǎng)站!宣布永久關(guān)閉
評論
圖片
表情
