4 MethodProbesAdapter
适配器,用于创建其他访问者事件以将探针插入到方法中。
@Override public void visitLabel(final Label label) { if (LabelInfo.needsProbe(label)) { if(tryCatchProbeLabels.containsKey(label)) { probesVisitor.visitLabel(tryCatchProbeLabels.get(label)); } probesVisitor.visitProbe(idGenerator.nextId()); } probesVisitor.visitLabel(label); } @Override public void visitInsn(final int opcode) { switch (opcode) { case Opcodes.IRETURN: case Opcodes.LRETURN: case Opcodes.FRETURN: case Opcodes.DRETURN: case Opcodes.ARETURN: case Opcodes.RETURN: case Opcodes.ATHROW: probesVisitor.visitInsnWithProbe(opcode,idGenerator.nextId()); break; default: probesVisitor.visitInsn(opcode); break; } } @Override public void visitJumpInsn(final int opcode, final Label label) { if (LabelInfo.isMultiTarget(label)) { probesVisitor.visitJumpInsnWithProbe(opcode,label, idGenerator.nextId(), frame(jumpPopCount(opcode))); } else { probesVisitor.visitJumpInsn(opcode,label); } } private int jumpPopCount(final int opcode) { switch (opcode) { case Opcodes.GOTO: return 0; case Opcodes.IFEQ: case Opcodes.IFNE: case Opcodes.IFLT: case Opcodes.IFGE: case Opcodes.IFGT: case Opcodes.IFLE: case Opcodes.IFNULL: case Opcodes.IFNONNULL: return 1; default: // IF_CMPxx and IF_ACMPxx return 2; } } @Override public void visitLookupSwitchInsn(final Label dflt, final int[]keys, final Label[] labels) { if (markLabels(dflt, labels)) { probesVisitor.visitLookupSwitchInsnWithProbes(dflt,keys, labels, frame(1)); } else { probesVisitor.visitLookupSwitchInsn(dflt,keys, labels); } } @Override public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label...labels) { if (markLabels(dflt, labels)) { probesVisitor.visitTableSwitchInsnWithProbes(min,max, dflt, labels, frame(1)); } else { probesVisitor.visitTableSwitchInsn(min,max, dflt, labels); } } 在MethodProbesAdapter中明显看到字节码指令信息,对于一个方法的进入,jvm中是一个方法栈的创建,入口指令是入栈指令,退出是return: privateint jumpPopCount(finalint opcode) { switch (opcode) { case Opcodes.GOTO: return0; caseOpcodes.IFEQ: caseOpcodes.IFNE: caseOpcodes.IFLT: caseOpcodes.IFGE: caseOpcodes.IFGT: caseOpcodes.IFLE: caseOpcodes.IFNULL: caseOpcodes.IFNONNULL: return1; default:// IF_CMPxx and IF_ACMPxx return2; } }
退出方法是return 指令:
publicvoid visitInsn(finalint opcode) { switch (opcode) { case Opcodes.IRETURN: caseOpcodes.LRETURN: caseOpcodes.FRETURN: caseOpcodes.DRETURN: caseOpcodes.ARETURN: caseOpcodes.RETURN: caseOpcodes.ATHROW: probesVisitor.visitInsnWithProbe(opcode,idGenerator.nextId()); break; default: probesVisitor.visitInsn(opcode); break; } } 逻辑跳转的有switch,if publicvoid visitTableSwitchInsn(finalint min, final int max, final Label dflt, final Label...labels) { if (markLabels(dflt, labels)) { probesVisitor.visitTableSwitchInsnWithProbes(min,max, dflt, labels, frame(1)); } else { probesVisitor.visitTableSwitchInsn(min,max, dflt, labels); } }
If分支:
case Opcodes.GOTO: return0; caseOpcodes.IFEQ: caseOpcodes.IFNE: caseOpcodes.IFLT: caseOpcodes.IFGE: caseOpcodes.IFGT: caseOpcodes.IFLE: caseOpcodes.IFNULL: caseOpcodes.IFNONNULL: return1; default:// IF_CMPxx and IF_ACMPxx return2; } LabelFlowAnalysis主要实现代码: @Override public void visitJumpInsn(final int opcode, final Label label) { LabelInfo.setTarget(label); if (opcode == Opcodes.JSR) { thrownew AssertionError("Subroutines not supported."); } successor = opcode != Opcodes.GOTO; first = false; } @Override public void visitLabel(final Label label) { if (first) { LabelInfo.setTarget(label); } if (successor) { LabelInfo.setSuccessor(label); } } @Override public void visitLineNumber(final int line, final Label start) { lineStart = start; } @Override public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label...labels) { visitSwitchInsn(dflt, labels); } @Override public void visitLookupSwitchInsn(final Label dflt, final int[]keys, final Label[] labels) { visitSwitchInsn(dflt, labels); } @Override public void visitInsn(final int opcode) { switch (opcode) { case Opcodes.RET: throw new AssertionError("Subroutinesnot supported."); case Opcodes.IRETURN: case Opcodes.LRETURN: case Opcodes.FRETURN: case Opcodes.DRETURN: case Opcodes.ARETURN: case Opcodes.RETURN: case Opcodes.ATHROW: successor = false; break; default: successor = true; break; } first = false; } 首先要知道对于一串指令比如: iLoad A; iLoad B; Add A,B; iStore; ……
如果没有跳转指令 GOTO LABEL或者jump,那么指令值按顺序执行的,所以我们只要在开始的时候添加一个探针就好,只要探针指令执行了,那么下面的指令一定会被执行的,除非有了跳转逻辑。因此我们只要在每一个跳转的开始和结束添加探针就好,就可以完全实现统计代码块的覆盖,而没有必要每一行都要植入探针。