基于javaAgent和ASM字节码技术跟踪java程序调用链 您所在的位置:网站首页 go函数调用链 基于javaAgent和ASM字节码技术跟踪java程序调用链

基于javaAgent和ASM字节码技术跟踪java程序调用链

2024-06-30 16:24| 来源: 网络整理| 查看: 265

作者:李家琦        评阅人:高邱雅   鹿凯翔

一、介绍 1. 目的

本文主要介绍如何使用javaAgent和ASM技术对java程序的方法调用进行跟踪,获得运行时方法之间的调用关系和方法的运行时间等信息,可以用于理解程序结构、了解方法实际执行时间、分析程序性能瓶颈等场景。使用javaAgent技术在程序加载字节码文件时,获取字节码并返回一个修改过的字节码文件,利用ASM技术可以对字节码进行增强,从而获取目标方法的运行状态。使用这种方式的好处是,可以在对代码没有入侵的情况下实现跟踪。

2. 环境

本项目中使用JDK8进行开发,并使用maven进行依赖管理。

二、 javaAgent技术介绍及使用

java.lang.Instrument包是在JDK5引入的,开发者可以通过修改方法的字节码实现动态修改类代码。下面先介绍一些相关概念:

JVMTI 首先需要介绍下JVMTI(JVM Tool Interface),它是JVM暴露出来的一些供开发者扩展的接口集合,当执行到某段程序时会调用某些回调接口,开发者可以利用这些接口扩展自己的逻辑。例如,在本项目中我们希望能够在JVM加载字节码时,获取到字节码并对其进行修改。

JVMTIAgent JVMTIAgent是一个动态库,它可以利用JVMTI暴露出的一些接口来实现一些特殊的功能。它有两种加载方式,可以在程序启动时加载,也可以在程序运行时动态进行加载。在我们使用eclipse、IDEA等IDE运行或者调试java代码时,它们就会在启动程序时加入相关参数,比如使用IDEA运行java程序,留意控制台最上方的输出,就会发现类似于如下的内容:

"D:\Programs\Java\jdk1.8.0_172\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.1\lib\idea_rt.jar=52832:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.1\bin" ......

instrument agent instrument是JVM 提供的一个 JVMTIAgent,windows环境下可以在jdk bin目录中找到一个叫做 instrument.dll的动态链接库。

使用instrument agent 首先需要实现一个包含premain的类,,如下所示

public class AopAgentTest { static private Instrumentation _inst = null; public static void premain(String agentArgs, Instrumentation inst) { Param.generatePARAMS(agentArgs); System.out.println("AopAgentTest.premain() was called."); /* Provides services that allow Java programming language agents to instrument programs running on the JVM.*/ _inst = inst; /* ClassFileTransformer : An agent provides an implementation of this interface in order to transform class files.*/ ClassFileTransformer trans = new AopAgentTransformer(); /*Registers the supplied transformer.*/ _inst.addTransformer(trans); } }

在premain方法中,可以获得一个Instrumentation对象,我们可以向其中加入一个ClassFileTransformer对象,Transformer对象的实现如下所示:

public class AopAgentTransformer implements ClassFileTransformer{ public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println("Transforming " + className); /* TODO 修改字节码,并返回修改后的字节码 */ byte[] transformed = classfileBuffer; return transformed; } }

这样就可以在JVM加载字节码文件时,获取到字节码并进行修改。然后,还需要在MANIFEST.MF中加入Premain-Class,并将程序打成jar包的形式(本项目中将程序的相关依赖也一并打进jar包),以便在目标程序中使用。使用maven插件进行打包,如下所示:

org.apache.maven.plugins maven-jar-plugin true lib/ true lib/ . com.nju.msr.core.instrument.AopAgentTest org.apache.maven.plugins maven-dependency-plugin copy package copy-dependencies ${project.build.directory}/lib

最后,在运行目标程序时,只需要添加 javaagent参数即可,如下所示:

-javaagent:./test.jar

需要注意的是,一个java程序中-javaagent这个参数的个数是没有限制的,所以可以添加任意多个javaagent。所有的javaagent会按照运行时的参数顺序执行。此外,javaagent需要放在包含main方法的jar包之前,否则javaagent不会起作用。每一个java agent 都可以接收一个字符串类型的参数,也就是premain中的agentArgs。

三、 ASM技术介绍及使用 首先我们需要引入如下的库: org.ow2.asm asm 6.2.1 org.ow2.asm asm-util 6.2.1 org.ow2.asm asm-commons 6.2.1 ASM使用观察者模式,依次访问类中的每个方法、属性,我们首先实现一个ClassVistior和MethodVisitor public class ClassAdapter extends ClassVisitor implements Opcodes { private String owner; private boolean isInterface; public ClassAdapter(final ClassVisitor cv) { super(ASM6, cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { cv.visit(version, access, name, signature, superName, interfaces); owner = name; isInterface = (access & Opcodes.ACC_INTERFACE) != 0; } @Override public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (!isInterface && mv != null && !"".equals(name) && !"".equals(name)) { mv = new MethodAdapter(mv, owner, access, name, desc, signature, exceptions); } return mv; } }

当访问到类中的每个方法时,会调用visitMethod方法,产生一个MethodVisitor对象来访问这个方法。在这里忽略了构造函数和类的静态代码块。对于java中的构造函数,我们知道它第一个调用的方法一定是父类的构造函数,当对字节码修改构造函数时,情况会有些复杂,这里暂不讨论。下面我们来实现一个MethodVisitor对象:

public class MethodAdapter extends MethodVisitor implements Opcodes { protected String className = null; protected int access = -1; protected String name = null; protected String desc = null; protected String signature = null; protected String[] exceptions = null; public MethodAdapter(final MethodVisitor mv, final String className, final int access, final String name, final String desc, final String signature, final String[] exceptions) { super(ASM6, mv); this.className = className; this.access = access; this.name = name; this.desc = desc; this.signature = signature; this.exceptions = exceptions; } @Override public void visitCode() { super.visitCode(); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;"); mv.visitLdcInsn("CALL classname:"+ className+ " access"+access+" name:" + name +" desc"+desc+" singature:"+signature); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } @Override public void visitInsn(int opcode) { if ((opcode >= Opcodes.IRETURN && opcode


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有