如何统计一个java程序执行的字节码数?

How to count the number of executed Bytecodes of a java program?

我正在尝试使用 Java ASM 编写一个简单的程序来计算执行的 Java 字节码的数量。

这个post问同样的问题,并且有使用ASM的解决方案。但是,答案指向一个不再可用的link。

我探索了 Java ASM API,找到了似乎允许此任务的基于树的 API。以下是我到目前为止所取得的成就。

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

public class Executor {

    public static void main(String[] args) throws IOException {
        Contract c = new Contract();
        c.execute();

        FileInputStream is = new FileInputStream("target/classes/Contract.class");

        ClassReader cr = new ClassReader(is);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

        final ClassNode classNode = new ClassNode();
        cr.accept(classNode, 0);
        classNode.fields.add(new FieldNode(Opcodes.ACC_PUBLIC,
                "Counter", "I", null, new Integer(-1)));


        FileOutputStream fos = new FileOutputStream("target/classes/ModifiedContract.class");
        classNode.accept(cw);
        fos.write(cw.toByteArray());
        fos.close();
    }


}

到目前为止我所取得的成就是将全局计数器添加到 .class 文件(字节码)。

据我了解,下一步是在每个字节码执行后添加一个 counter++ 语句。

从文档中我发现 MethodNode 中的 InsnList 对象对应一个字节码:这是正确的吗?

如果是这样,我可以为每个字节码添加一个 counter++ 语句的最佳方法是什么,以便最后我得到执行的字节码数(我对字节码总数不感兴趣,但只对执行的字节码感兴趣字节码)

(我是 Java + ASM 世界的新手)

谢谢

@Holger 评论的详细说明

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.ListIterator;

public class Executor {

    public static void main(String[] args) throws IOException {
        Contract c = new Contract();
        c.execute();

        FileInputStream is = new FileInputStream("target/classes/Contract.class");

        ClassReader cr = new ClassReader(is);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

        final ClassNode classNode = new ClassNode();
        cr.accept(classNode, 0);
        classNode.fields.add(new FieldNode(Opcodes.ACC_STATIC +Opcodes.ACC_PUBLIC, "count", "I", null, 0));


        for (final MethodNode mn :classNode.methods) {

            ListIterator<AbstractInsnNode> itr = mn.instructions.iterator();

            // core insert
            while (itr.hasNext()) {

                AbstractInsnNode node = itr.next();

                InsnList numCounting = new InsnList();

                // insert count++
                numCounting.add(new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, "count", "I"));
                numCounting.add(new InsnNode(Opcodes.ICONST_1));
                numCounting.add(new InsnNode(Opcodes.IADD));
                numCounting.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, "count", "I"));

                // insert callme
//            numCounting.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/lvxiaoxin/staticDemo", "callme", "()V", false));

                mn.instructions.insert(node, numCounting);
            }
        }


        FileOutputStream fos = new FileOutputStream("target/classes/Contract.class");
        classNode.accept(cw);
        fos.write(cw.toByteArray());
        fos.close();
    }


}

基本上这会在每个字节码执行后添加一个 count++ 语句。

作为示例,将以下 class 视为原始 java class。

public class Contract {
    private double Cvar1;
    private float Cvar2;
    private char Cvar3;
    private String Cvar4;
    private int Cvar5;
    private long Cvar6;

    public Contract(){}

    public void execute(){
        double var1 = 0.1;
        float var2 = 0.1F;
        char var3 = 'a';
        String var4 = "a";
        int var5 = 1;
        long var6 = 2;
        System.out.println("Executing the contract");
    }


}

现在,如果这个 class 被编译,并且使用执行器修改生成的字节码文件,则会生成以下反编译 class。

// // IntelliJ IDEA 从 .class 文件重新创建的源代码 //

import java.io.PrintStream;

public class Contract {
    private double Cvar1;
    private float Cvar2;
    private char Cvar3;
    private String Cvar4;
    private int Cvar5;
    private long Cvar6;
    public static int count;

    public Contract() {
        ++count;
        ++count;
        ++count;
        super();
        ++count;
    }

    public void execute() {
        ++count;
        ++count;
        ++count;
        double var1 = 0.1D;
        ++count;
        ++count;
        ++count;
        ++count;
        float var3 = 0.1F;
        ++count;
        ++count;
        ++count;
        ++count;
        boolean var4 = true;
        ++count;
        ++count;
        ++count;
        ++count;
        String var5 = "a";
        ++count;
        ++count;
        ++count;
        ++count;
        boolean var6 = true;
        ++count;
        ++count;
        ++count;
        ++count;
        long var7 = 2L;
        ++count;
        ++count;
        ++count;
        PrintStream var10000 = System.out;
        ++count;
        ++count;
        var10000.println("Executing the contract");
        ++count;
        ++count;
        ++count;
    }
}