【问题标题】:How to count the number of executed Bytecodes of a java program?如何计算java程序执行字节码的数量?
【发布时间】:2022-01-03 07:16:26
【问题描述】:

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

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

我探索了 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 世界的新手)

谢谢

【问题讨论】:

  • 您不想将该字段声明为static吗?进一步注意,在这里指定一个常量值是没有意义的,因为该字段不是常量。如果你想要一个可变字段的初始值,你必须在构造函数中分配它(或者如果字段是static,则为类初始化器)。但是为什么不使用默认值零,这是一个适合计数器的初始值呢?然后,增加一个static 字段包含四个指令,getstaticiconst_1iaddputstatic,而增加一个非static 字段需要六个:aload_0getfield、添加一、aload_0putfield
  • @Holger 感谢您的评论。我认为你是正确的,我应该将它声明为一个类变量 - 静态的。我不确定我是否正确理解了您增加静态计数器的方法。可以给个代码sn-p吗?
  • 一般来说,当你想做字节码操作的时候,你应该知道The Java® Virtual Machine Specification。第 6 章描述了所有字节码指令,但第 3 章也很有价值,因为它展示了高级构造如何映射到这些指令。了解其他章节中的其他内容也不会有什么坏处……

标签: java bytecode java-bytecode-asm


【解决方案1】:

@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++ 语句。

例如,将以下类视为原始 java 类。

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");
    }


}

现在如果编译了这个类,并且使用 Executor 修改了生成的 Bytecode 文件,就会生成下面的 Decompiled 类。

// // 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;
    }
}

【讨论】:

  • 请注意,由于您没有引入新的分支或局部变量,因此您不需要相当昂贵的 COMPUTE_FRAMES 选项;原始帧已经足够了。 COMPUTE_MAXS 会在这里做。此外,将try-with-resources 用于FileInputStream,尤其是FileOutputStream。或者首先使用Files.readAllBytes(Path)Files.write(Path, byte[], …)
猜你喜欢
  • 1970-01-01
  • 2017-07-02
  • 2014-09-26
  • 2019-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多