【字节码编程】统计方法执行次数:ASM修改JDK (java/util/regex/Pattern$Loop)class文件

31 篇文章 0 订阅
4 篇文章 0 订阅

0 背景: 检测用户发表评论是否全是表情符,具体代码参考:https://blog.csdn.net/qfzhangwei/article/details/104138979  ,由于使用了正则匹配,用户输入小红旗时,遇到了匹配超长时间的问题。于是想看看那那个到底匹配了到多少次,但是用的Pattern类红的方法,没法直接统计调用次数,但是可以通过ASM 修改字节码。

1 修改的java代码

private int mcounter = 0;

public void do2() {

System.out.println(mcounter++);

}

2 获取ASM源代码 (工具ASMifierClassVisitor)

java -classpath /Users/sss/.m2/repository/org/glassfish/hk2/external/asm-all-repackaged/2.1.81/asm-all-repackaged-2.1.81.jar org.objectweb.asm.util.ASMifierClassVisitor /Users/sss/***/asm/AppendMethod/Cost.class

3 将方法的代码指令修改(需要根据修改的类做调整)

3 使用arthas 工具 jad 查看修改后source代码

5 占满一个核心

6 stack栈

7 核心代码:

public class Main {
    public static void main(String[] args) throws IOException {
        String text = "\uD83C\uDDF2\uD83C\uDDEA\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDE8\uD83C\uDDF3\uD83C\uDDEC\uD83C\uDDE7\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73\uDB40\uDC7F\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74\uDB40\uDC7F\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67\uDB40\uDC7F";
        boolean allEmoji = EmojiUtil.isAllEmoji(text);
        System.out.println(allEmoji);
    }
    public static void premain(String args, Instrumentation inst) {
        inst.addTransformer(new MethodCallCountTransformerCustom());
    }

}

// class
public class MethodCallCountTransformerCustom implements ClassFileTransformer {


    @Override
    public byte[] transform(ClassLoader loader,
                            String className,
                            Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain,
                            byte[] classfileBuffer){
        try {
            if (!(className).contains("java/util/regex/Pattern$Loop")) {
                return classfileBuffer;
            }else{
                System.out.println(className);
            }
            ClassReader reader = new ClassReader(classfileBuffer);
            ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
            ClassAdapter adapter = new MethodCallClassAdapter(writer, className);
            reader.accept(adapter, 0);
            return writer.toByteArray();
        }catch (Exception e){
            return classfileBuffer;
        }
    }

}

// class adapter 字节码过滤器
public class MethodCallClassAdapter extends ClassAdapter {
    private String className;
    public MethodCallClassAdapter(ClassVisitor cv,String className) {
        super(cv);
        this.className = className;
    }

    @Override
    public MethodVisitor visitMethod(final int access, final String name,
                                     final String desc, final String signature, final String[] exceptions) {
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions);
        MethodVisitor wrappedMv = mv;
        if (mv != null) {
            if (name.equalsIgnoreCase("match")) {
                System.out.println(name);
                // 使用自定义 MethodVisitor,实际改写方法内容
                wrappedMv = new AppendFieldToPatternMethodAdapter(mv);
            }
        }
        return wrappedMv;
    }

    private String fName = "mcounter";
    private boolean isFieldPresent;

    @Override
    public FieldVisitor visitField(int access, String name, String desc,
                                   String signature, Object value) {
        if (name.equals(fName)) {
            isFieldPresent = true;
        }
        return cv.visitField(access, name, desc, signature, value);
    }

    @Override
    public void visitEnd() {
        if (!isFieldPresent) {
            FieldVisitor fv = cv.visitField(Opcodes.ACC_PRIVATE, fName, "I", null, null);
            if (fv != null) {
                fv.visitEnd();
            }
        }
        cv.visitEnd();
    }


}
// write 
import static org.objectweb.asm.Opcodes.*;

public class AppendFieldToPatternMethodAdapter extends MethodAdapter {
    public AppendFieldToPatternMethodAdapter(MethodVisitor mv) {
        super(mv); 
    }

    @Override
    public void visitEnd() {
        super.visitEnd();
    }

    @Override
    public void visitCode() {

        // Pattern$Loop 类覆盖visitCode方法
        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv.visitVarInsn(ALOAD, 0);
        mv.visitInsn(DUP);
        mv.visitFieldInsn(GETFIELD, "java/util/regex/Pattern$Loop", "mcounter", "I");
        mv.visitInsn(DUP_X1);
        mv.visitInsn(ICONST_1);
        mv.visitInsn(IADD);
        mv.visitFieldInsn(PUTFIELD, "java/util/regex/Pattern$Loop", "mcounter", "I");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V");

    }
}

8 agent jar  参考 https://blog.csdn.net/qfzhangwei/article/details/105181355

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自驱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值