CN116541282A - 一种Java进程运行期间动态代码栈分析观察方法 - Google Patents

一种Java进程运行期间动态代码栈分析观察方法 Download PDF

Info

Publication number
CN116541282A
CN116541282A CN202310502766.9A CN202310502766A CN116541282A CN 116541282 A CN116541282 A CN 116541282A CN 202310502766 A CN202310502766 A CN 202310502766A CN 116541282 A CN116541282 A CN 116541282A
Authority
CN
China
Prior art keywords
class
com
freez
enhancement
java
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Pending
Application number
CN202310502766.9A
Other languages
English (en)
Inventor
冒懋
龚旭
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
Nanjing Xinmi Network Technology Co ltd
Original Assignee
Nanjing Xinmi Network Technology Co ltd
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by Nanjing Xinmi Network Technology Co ltd filed Critical Nanjing Xinmi Network Technology Co ltd
Priority to CN202310502766.9A priority Critical patent/CN116541282A/zh
Publication of CN116541282A publication Critical patent/CN116541282A/zh
Pending legal-status Critical Current

Links

Classifications

    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F11/00Error detection; Error correction; Monitoring
    • G06F11/36Preventing errors by testing or debugging software
    • G06F11/362Software debugging
    • G06F11/3636Software debugging by tracing the execution of the program
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F11/00Error detection; Error correction; Monitoring
    • G06F11/36Preventing errors by testing or debugging software
    • G06F11/3604Software analysis for verifying properties of programs
    • G06F11/3612Software analysis for verifying properties of programs by runtime analysis
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F11/00Error detection; Error correction; Monitoring
    • G06F11/36Preventing errors by testing or debugging software
    • G06F11/362Software debugging
    • G06F11/3624Software debugging by performing operations on the source code, e.g. via a compiler
    • YGENERAL TAGGING OF NEW TECHNOLOGICAL DEVELOPMENTS; GENERAL TAGGING OF CROSS-SECTIONAL TECHNOLOGIES SPANNING OVER SEVERAL SECTIONS OF THE IPC; TECHNICAL SUBJECTS COVERED BY FORMER USPC CROSS-REFERENCE ART COLLECTIONS [XRACs] AND DIGESTS
    • Y02TECHNOLOGIES OR APPLICATIONS FOR MITIGATION OR ADAPTATION AGAINST CLIMATE CHANGE
    • Y02DCLIMATE CHANGE MITIGATION TECHNOLOGIES IN INFORMATION AND COMMUNICATION TECHNOLOGIES [ICT], I.E. INFORMATION AND COMMUNICATION TECHNOLOGIES AIMING AT THE REDUCTION OF THEIR OWN ENERGY USE
    • Y02D10/00Energy efficient computing, e.g. low power processors, power management or thermal management

Landscapes

  • Engineering & Computer Science (AREA)
  • Theoretical Computer Science (AREA)
  • Computer Hardware Design (AREA)
  • Quality & Reliability (AREA)
  • Physics & Mathematics (AREA)
  • General Engineering & Computer Science (AREA)
  • General Physics & Mathematics (AREA)
  • Software Systems (AREA)
  • Stored Programmes (AREA)

Abstract

本发明公开了一种Java进程运行期间动态代码栈分析观察方法,属于大数据技术领域,包括解除VirtualMachine API和AttachProvider API的限制,Agentlib的动态生成与加载和得到专利定义的Agentlib获取编码器的能力,增强符合用户要求的类class,兼容已存在的firstAgentlib,可以自动挂载agentlib并且兼容mac/linux/windwos,无需手动在启动命令中添加参数,提供额外jar包,提供了默认增强接口,可以精确控制要关注的类对应代码运行细节,兼容其他agentlib,可以和第三方agentlib共存,使用便捷。

Description

一种Java进程运行期间动态代码栈分析观察方法
技术领域
本发明属于大数据技术领域,尤其涉及一种Java进程运行期间动态代码栈分析观察方法。
背景技术
目前java编写的代码上线后,因为安全或者其它部署方式等原因,是完全封闭式环境。
不允许进程开放远程调试,这样会导致无法使用java debug工具调试,也不允许后门端口,植入命令禁止热替换,避免植入恶意代码,这样导致的后果是:出现问题后只能通过观察日志或者环境复现等其它方式,然而日志往往打印不全面,并且补全日志后,意味着要重新安装部署java进程,重启进程。
现有技术的缺陷和不足:
参考阿里云提供的监控方式,首先需要额外启动arthas-boot.jar进程,并且还要部署到对应机器,如果机器是客户提供的,这会很麻烦,客户不一定同意多启动一个不知名进程。
arthas-boot.jar进程需要入挂载到math-game.jar中,这意味着用户执行权限要比math-game.jar高。
通过watch命令,可以看出他观察的局限性,属于进程运行后,后期通过watch对代码的增强,假设用户要观察某个方法包含循环语句,那么无法获取观察结果,这是因为增强代码因为循环中并未执行结束而无法生效。
发明内容
本发明的目的是提供一种Java进程运行期间动态代码栈分析观察方法,解决了现有技术的不足。
为实现上述目的,本发明采用如下技术方案:一种Java进程运行期间动态代码栈分析观察方法,包括如下步骤:
步骤1:解除VirtualMachine API和AttachProvider API的限制,根据操作系统的类型来建立匹配的AttachProvider,将Virtua lMachine API中的providers()方法逻辑的默认返回的com.sun.to ols.attach.spi.AttachProvider修改为com.freez.utils.attach.spi.AttachProvider,以允许外界访问;
通过调用attachProvider.attachVirtualMachine()方法获取virtualMachine
通过调用virtualMachine.loadAgent()方法来动态加载agentlib;
步骤2:Agentlib的动态生成与加载和得到专利定义的Agentlib获取编码器的能力,具体步骤包括通过Agentlib API支持完成字节码增强;通过Agentlib获取java.lang.instrument.Instrumentation编码器;通过java.lang.instrument.Instrumentation.redefineClasses()方法在字节码增强后重新定义Class类;
步骤3:增强符合用户要求的类class,具体包括解除ASM API调用限制,建立增强接口com.freez.utils.clazz.WeaveSpy,增强接口接口com.freez.utils.clazz.WeaveSpy的默认行为是在某个方法被执行到任意阶段时,记录日志信息,建立类委托器com.freez.utils.clazz.ClassVisitorWeaver,用于植入com.freez.utils.clazz.We aveSpy类,获取用户规定的要增强的类的原始字节码集合,循环对每个字节码通过ASM API进行字节码增强;
步骤4:兼容已存在的firstAgentlib,具体包括如下步骤:
步骤S4-1:为了区别Agentlib,定义其它的Agentlib为firstAg entlib,firstAgentlib对应的转换器为firstTransformer。先判断是否存在其它的Agentlib,如果存在,则获取转换器firstTransformer,具体包括先遍历代码命令行判断是否包含agentlib配置,如果包含则获取配置的对应java.lang.instrument.Instrumentation编码器,然后再获取编码器中的转换器firstTransformer;
步骤S4-2:当使用专利定义Agentlib进行字节码增强时,如果存在firstTransformer,先用firstTransformer进行字节码增强,再用专利定义的Agentlib进行二次增强。
优选的,在执行步骤1时,具体包括如下步骤:
步骤S1-1:通过java spi文件加载机制,找出针对不同操作系统的多个默认配置文件,将所有默认配置文件均放入同一个软件包中,生成配置软件包,并替换SPI文件中的默认提供者为com.freez.utils.attach.FreezAttachProvider;
步骤S1-2:在com.freez.utils.attach.FreezAttachProvide中,根据操作系统的类型来建立匹配的AttachProvider;
步骤S1-3:在VirtualMachine API中,先将复制jdk原始类com.sun.tools.attach.VirtualMachine复制一份到com.freez.utils.attach.spi.VirtualMachine,然后将providers()方法逻辑所默认返还的com.sun.tools.attach.spi.AttachProvider替换为返回com.freez.utils.attach.spi.AttachProvider,以允许外界访问,此时限制解除才可以通过virtualMachine.loadAgent()方法来动态加载agentlib;
优选的,在执行步骤2时,具体步骤如下:
步骤S2-1:将agentlib配置文件和编译器接类com.freez.utils.clazz.Agent$Less放入temp.jar中,然后在java进程启动时,将temp.jar重新命名为随机数.jar,随机数.jar即为本专利的agentlib,最后将随机数.jar加载到virtualMachine中;
步骤S2-2:在随机数.jar加载后,即本专利agentlib加载成功后,将com.freez.utils.clazz.Agent$Less.agentmain()方法传入java.lang.instrument.Instrumentation编码器,从而获得java.lang.instrument.Instrumentation编码器,拥有重新定义类的能力。
优选的,在执行步骤3时,具体包括如下步骤:
步骤S3-1:解除ASM API调用限制;具体包括找出jdk的jdk.internal.org.objectweb.asm软件包中的AMS API,将openjdk的对应源码复制到com.freez.utils.objectweb.asm包内,以得到ASM API调用权限;
步骤S3-2:增强接口接口com.freez.utils.clazz.WeaveSpy的默认行为是某个方法被执行到任意阶段时记录日志信息;com.freez.utils.clazz.WeaveSpy接口将被植入到用户待增强的类中;
在增强类所被调用方法进入时调用com.freez.utils.clazz.WeaveSpy._onMethodEnter()方法;
在增强类所被调用方法结束调用com.Freez.utils.clazz.WeaveSpy._onMethodExit()方法;
在增强类所被调用方法运行中com.freez.utils.clazz.WeaveSpy._invokeLine()方法;
在增强类所被调用方法发生异常时调用com.freez.utils.clazz.WeaveSpy._onMethodThrowing()方法;
步骤S3-3:建立类委托器com.freez.utils.clazz.ClassVisitorWeaver,用于植入com.freez.utils.clazz.WeaveSpy类,具体包括在类委托器解析某个原始字节码中的任何方法时,调用visitMethod方法进行方法解析;在解析方法开始行为时调用onMethodEnter方法,在onMethodEnter调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodEnter方法;
在解析方法内容每一行代码时,调用visitLineNumber方法,在visitLineNumber调用时通过ASM API植入增强接口com.freez.utils.clazz.WeaveSpy._invokeLine方法;
在解析方法结束行为时,调用onMethodExit方法,判断增强类方法执行结果,如果未发生异常在onMethodExit调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodExit方法,如果发生了异常在onMethodExit调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodThrowing方法;
步骤S3-4:获取用户规定的要增强的类的原始字节码集合;
步骤S3-5:遍历原始字节码集合,循环对每个字节码通过ASM API进行字节码增强。
优选的,在执行步骤S3-4时,具体包括如下步骤:
步骤S3-4-1:定义核心类为com.freez.utils.clazz.Point,核心类提供了publicstatic void open(Set<String>scanPackageSet)方法,核心类传入的参数定义了要扫描的java软件包名集合,假设用户调用open传入包名为“com.freez.mq.broker”,对java包名为com.freez.mq.broker开头的所有类和子类进行扫描;
步骤S3-4-2:通过com.freez.utils.clazz.Point.open()方法打开本软件功能,对符上述条件的类字节码增强,并且提供跟踪分析观察能力;
步骤S3-4-3:对所有的类进行扫描然后根据用户规定进行匹配,并且读取匹配上的类对应的字节码信息,获取类加载器对象;
步骤S3-4-4:获取类加载器对应类;
步骤S3-4-5:获取类加载器的ucp字段,ucp记录了所有的类路径;
步骤S3-4-6:获取类加载器的字段ucp(urlClassPath)的值;
步骤S3-4-7:获取urlClassPath的getURLs获取类路径;
步骤S3-4-8:获取类路径中的class文件或者jar文件或者文件夹;
步骤S3-4-9:递归处理这些文件,获取对应的类文件的原始字节码然后增强。
本发明所述的一种Java进程运行期间动态代码栈分析观察方法,可以自动挂载agentlib并且兼容mac/linux/windwos,无需手动在启动命令中添加参数,提供额外jar包,提供了默认增强接口,可以通过任何一个slf4j日志提供商,就可以精确控制要关注的类对应代码运行细节,兼容其他agentlib,可以和第三方agentlib共存,使用便捷,本发明的所有实现都可以封装在一个freez-utils。jar包中,无需用户再引入其他任何软件包,将需要进行代码分析观察的项目引入freez-utils。jar,然后在启动类通过com。freez。utils。clazz。Point。open()方法打开本发明功能,就可以通过日志文件观察代码的运行详细轨迹、耗时、执行线程等信息。
附图说明
图1是本发明启动时的流程图;
图2是本发明工作状态下的流程图;
图3是某javaWeb网站,随着网站用户变多后网站页面变的卡顿,通过使用本发明分析其卡顿案例图。
具体实施方式
术语解释
Java编译,java源文件为.java结尾的文件,比如test.java;在这个文件中有源代码,和源代码中编写的方法,字段对应的文件行。
Java源文件可以用jdk自带的javac软件进行编译;
编译后生成.class结尾的文件,比如test.class;这个文件是二进制的,不可以阅读,这个文件的二进制内容叫字节码(byte数组)
.class文件,可以用反编译工具,获取源码内容,这个过程叫反编译。
字节码也可以进行解析以便获取所定义的类,方法,字段等,一般的解析工具有ASM。
字节码解析后可以进行修改,修改后又可以重新编译为新的字节码,这个过程叫字节码增强,一般增强工具有ASM。
方法,java中运行代码的最小执行单元就是方法,方法类似于C++的函数概念,java中的方法可以调用其他方法和生命变量,方法的生命周期包括方法开始,方法运行中,方法结束/异常。
SLF4J,SLF4J是java,打印日志的API,提供用户通过日志名称,获取对应日志的能力。
org.slf4j.LoggerFactory.getLogger(String logName)
用户可以不同的日志名称,选择对应日志是否开启,日志级别和打印到何处(磁盘,文件,流)中,一般这些控制都基于特定的配置文件,并且配置文件一般支持实时修改生效。
ASM是一套对java类对应字节码内容,进行重新编译修改的API,java提供的jdk中内置了一套ASM API,位于jdk.internal.org.objectweb.asm软件包中
提供类解析委托接口jdk.internal.org.objectweb.asm.ClassVisitor,这个接口的能力是,在解析字节码时,每解析到任何地方,会进行通知,(比如读取到一个字段时通知,读取到方法时通知,读取到方法中变量时通知等。)。可以在收到通知时,对原始字节码进行修改。
提供类编写器字节码增强后输出接口jdk.internal.org.objectweb.asm.ClassWriter,该接口的建立需要通过ClassReader,通过ClassWriter的toByteArray()方法,可以获取原始字节码的增强字节码。
提供类阅读器字节码读取解析接口jdk.internal.org.objectweb.asm.ClassReader.该接口的accept(ClassVisitor classVisitor)方法可以根据传入的ClassVisitor(类委托器)将增强的内容填充到ClassReader中。
字节码增强是java语言中,用class类对应生效的字节码,通过ASM(或者类似ASM的API)将原始类信息,进行重新二次修改(修改方法,替换方法等)的能力
反射是java常用的一套API,位于jdk内置java.lang.reflect包下和常见能力包括方法,字段的执行和访问,java很多类或者方法是不提供外部正常访问的,这时可以利用反射来访问。除了java.lang.reflect包下面提供的类,还有2个类也提供了反射相关能力。
java.lang.ClassLoader类加载器,Java通过其加载一个class(类)
java.lang.Class用于描述类,java每个对象都叫做某个对应类的实例.Java通过其获取对应类的方法,字段等。
Agentlib是java语言中,用于通过将特定jar包文件,通过java命令行参数–agentlib或者通过java虚拟机API,将agentlib植入到java虚拟机中,以便获取java.lang.instrument.Instrumentation(java编码器)的能力.植入成功后,java进程启动时,将会把java.lang.instrument.Instrumentation(java编码器)对象作为参数提供给agentlib配置文件中所配置的编译器接收类。
java编码器java.lang.instrument.Instrumentation(java编码器)是java语言中的,用于重新定义类,重新转换类的一套API,其中包含如下两个函数用于转换或者定义:
retransformClasses(Class<?>...classes)throws UnmodifiableClassException;
redefineClasses(ClassDefinition...definitions)throwsClassNotFoundException,UnmodifiableClassException。
如图1所述的一种Java进程运行期间动态代码栈分析观察方法,包括如下步骤:
步骤1:解除VirtualMachine API和AttachProvider API的限制,根据操作系统的类型来建立匹配的AttachProvider,将VirtualMachine API中的providers()方法逻辑的默认返回的com.sun.to ols.attach.spi.AttachProvider修改为com.freez.utils.attach.spi.AttachProvider,以允许外界访问,这样才能通过调用virtualMachine.loadAgent()方法来动态加载agentlib;
在执行步骤1时,具体包括如下步骤:
步骤S1-1:通过java spi文件加载机制,找出针对不同操作系统的多个默认配置文件,将所有默认配置文件均放入同一个软件包中,生成配置软件包,并替换SPI文件中的默认提供者为com.freez.utils.attach.FreezAttachProvider;
步骤S1-2:在com.freez.utils.attach.FreezAttachProvide中,根据操作系统的类型来建立匹配1的AttachProvider;
步骤S1-3:在VirtualMachine API中,先将复制jdk原始类com.sun.tools.attach.VirtualMachine复制一份到com.freez.utils.attach.spi.VirtualMachine,然后将providers()方法逻辑所默认返还的sun替换为返回com.freez.utils.attach.spi.AttachProv ider,以允许外界访问,到此限制解除,才可以通过virtualMachin e.loadAgent()方法来动态加载agentlib;
本实施例中,AttachProvider API的能力是支持将agentlib可以挂载到允许的JVM中.这个AIP的默认实现根据你针对不同操作系统下载的JDK而进行不同配置,通过javaspi文件加载机制,可以找到默认配置文件,下面给出4个不同操作系统,对应的默认配置[solaris]sun.tools.attach.SolarisAttachProvider(jdk内部对solaris操作系统默认配置)
[linux]sun.tools.attach.LinuxAttachProvider(jdk内部对linux操作系统默认配置)
[macosx]sun.tools.attach.BsdAttachProvider(jdk内部对苹果操作系统默认配置)
[windows]sun.tools.attach.WindowsProvider(jdk内部对windows操作系统默认配置)
但是因为这个API是sun的内部代码,不作为jdk开源接口,并且对于不同操作系统,他提供的实现类还不一样,所以本发明将上述四个类都复制到本发明的软件包中,这样不需要担心所使用的操作系统不同,并且本发明替换了SPI文件中的默认提供者为com.freez.utils.attach.FreezAttachProvider.
在这个com.freez.utils.attach.FreezAttachProvider中,本发明会根据判断操作系统的不同,来建立匹配的AttachProvider实现:
VirtualMachine API提供了寻找AttachProvider的能力,但是因为这个API是sun的内部代码,不作为jdk开源接口,所以本发明先将复制jdk原始类com.sun.tools.attach.VirtualMachine复制一份到com.freez.utils.attach.spi.VirtualMachine.并且替换了providers()方法逻辑,修改为返回com.freez.utils.attach.FreezAttachProvider实例。
到此限制就可以解除,本发明就可以通过调用virtualMachine.loadAgent()方法来动态加载agentlib。
步骤2:Agentlib的动态生成与加载和得到专利定义的Agentlib获取编码器的能力,具体步骤包括通过Agentlib API支持完成字节码增强;通过Agentlib获取java.lang.instrument.Instrumentation编码器;通过java.lang.instrument.Instrumentation.rede fineClasses()方法在字节码增强后重新定义Class类;
具体步骤如下:
步骤S2-1:通过Agentlib API支持完成字节码增强,字节码增强即为将一个class的字节码,重新解析,插入新的代码块.然后再次编译;
步骤S2-2:利用java的jar包Agentlib中的配置,获取Instr umentation的类名和配置文件;
步骤S2-3:将配置文件和com.freez.utils.clazz.Agent$Less放入temp.jar中,然后在java进程启动时,将temp.jar重新命名为随机数.jar,随机数.jar即为专利定义的agentlib,最后将随机数.jar加载到virtualMachine中;
步骤S2-4:在随机数.jar加载后,即本专利的agentlib加载成功后,将类名.agentmain()方法传入java.lang.instrument.Instru mentation编码器,从而获得java.lang.instrument.Instrumentation编码器,拥有重新定义类的能力。
本实施例中,为了完成字节码增强(将一个class的字节码,重新解析,插入新的代码块.然后再次编译就叫增强).需要通过Agentlib API支持,Agentlib可以获取java.lang.instrument.Instru mentation(编码器),通过java.lang.instrument.Instrumentation.redefineClasses()方法在字节码增强后重新定义Class(类).
Agentlib是一个java的jar包,jar包中有个配置,定义了获取Instrumentation的类名.本实施例中这里配置的类名是com.freez.utils.clazz.Agent$Less.配置文件信息内容具体如下:
Agent-Class:com.freez.utils.clazz.Agent$Less
Can-Redefine-Classes:true
Can-Retransform-Classes:true
将配置文件和com.freez.utils.clazz.Agent$Less类提前放入temp.jar中,然后在java进程启动时,本发明把temp.jar重新命名为(随机数).jar,然后加载到virtualMachine中。
Agentlib加载后,会将com.freez.utils.clazz.Agent$Less.agentmain()方法传入java.lang.instrument.Instrumentation(编码器)。
到此获取了到了java.lang.instrument.Instrumentation(编码器),拥有了重新定义类的能力。
步骤3:增强符合用户要求的类class,具体包括如下步骤:
步骤S3-1:解除ASM API调用限制;具体包括找出jdk的jdk.internal.org.objectweb.asm软件包中的AMS API,将openjdk的对应源码复制到com.freez.utils.objectweb.asm包内,以得到ASM API调用权限;
步骤S3-2:定义增强接口com.freez.utils.clazz.WeaveSpy,具体包括接口默认行为主要包括在某个方法被执行到任意阶段时,记录日志信息;com.freez.utils.clazz.WeaveSpy接口将被植入到用户待增强的类中;在增强类所被调用方法进入时调用com.freez.utils.clazz.WeaveSpy._onMethodEnter方法;
在增强类所被调用方法结束调用com.freez.utils.clazz.WeaveSpy._onMethodExit方法;
在增强类所被调用方法运行中com.freez.utils.clazz.WeaveSpy._invokeLine方法;
在增强类所被调用方法发生异常时调用com.freez.utils.clazz.WeaveSpy._onMethodThrowing方法;
步骤S3-3:建立类委托器com.freez.utils.clazz.ClassVisito rWeaver,用于字节码增强,具体包括在类委托器解析某个原始字节码中的任何方法时,调用visitMethod方法进行方法解析;在解析方法开始行为时调用onMethodEnter方法,在onMethodEnter调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodEnter方法;
在解析方法内容每一行代码时,调用visitLineNumber方法,在visitLineNumber调用时通过ASM API植入增强接口com.freez.utils.clazz.WeaveSpy._invokeLine方法;
在解析方法结束行为时,调用onMethodExit方法,判断增强类方法执行结果,如果未发生异常在onMethodExit调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodExit方法,如果发生了异常在onMethodExit调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodThrowing方法;
步骤S3-4:获取用户规定的要增强的类的原始字节码集合,具体包括
步骤S3-4-1:定义核心类为com.freez.utils.clazz.Point,核心类提供了publicstatic void open(Set<String>scanPackageSe t)方法,核心类传入的参数定义了要扫描的java软件包名集合,假设用户调用open传入包名为“com.freez.mq.broker”,将会对java包名为com.freez.mq.broker开头的所有类和子类进行扫描;
步骤S3-4-2:通过com.freez.utils.clazz.Point.open()方法打开本软件功能,对符上述条件的类字节码增强,并且提供跟踪分析观察能力;
步骤S3-4-3:对所有的类进行扫描然后根据用户规定进行匹配,并且读取匹配上的类对应的字节码信息,获取类加载器对象;
步骤S3-4-4:获取类加载器对应类;
步骤S3-4-5:获取类加载器的ucp字段,ucp记录了所有的类路径;
步骤S3-4-6:获取类加载器的字段ucp(urlClassPath)的值;
步骤S3-4-7:获取urlClassPath的getURLs获取类路径;
步骤S3-4-8:获取类路径中的class文件或者jar文件或者文件夹;
步骤S3-4-9:递归处理这些文件,获取对应的类文件的原始字节码;
步骤S3-5:遍历原始字节码集合,循环对每个字节码通过ASM API进行字节码增强。
本实施例中,解除ASM API调用限制
AMS API.默认位于jdk的jdk.internal.org.objectweb.asm软件包中,但是因为这个API是sun的内部代码,不作为jdk开源接口,所以我将openjdk的对应源码复制到com.freez.utils.objectweb.asm包下.到此本发明得到了ASM API调用权限。
定义增强接口com.freez.utils.clazz.WeaveSpy。
接口默认行为主要包括在某个方法被执行到任意阶段时,记录日志信息.com.freez.utils.clazz.WeaveSpy接口将被植入到用户待增强的类中。
在增强类所被调用方法进入时调用com.freez.utils.clazz.WeaveSpy._onMethodEnter方法。
在增强类所被调用方法结束调用com.freez.utils.clazz.WeaveSpy._onMethodExit方法。
在增强类所被调用方法运行中com.freez.utils.clazz.WeaveSpy._invokeLine方法。
在增强类所被调用方法发生异常时调用com.freez.utils.clazz.WeaveSpy._onMethodThrowing方法。
定义类委托器com.freez.utils.clazz.ClassVisitorWeaver,用于本方法的字节码增强。
ClassVisitorWeaver是com.freez.utils.objectweb.asm.ClassVisitor的具体实现类,我覆盖了其visitMethod(int access,String methodName,String methodDdesc,String signature,String[]exceptions)方法实现,用于方法增强。
在类委托器解析某个原始字节码中的任何方法时,将会调用visitMethod方法进行方法解析。
在解析方法开始行为时会调用onMethodEnter方法,本发明在on MethodEnter调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodEnter方法。下文仅举例植入过程,
protected void onMethodEnter()
{
//~~~植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodEnter方法
~~~
//生成静态方法com.freez.utils.clazz.WeaveSpy._onMethodEnter
//ON_METHOD_ENTER_CALL为
com.freez.utils.clazz.WeaveSpy._onMethodEnter描述
getStatic(Type.getType(WeaveSpy.class),"ON_METHOD_ENTER_CALL",
METHOD_Type);
//将上述方法入栈
push((Type)null)
//构造com.freez.utils.clazz.WeaveSpy._onMethodEnter的参数数组.
push(6);
newArray(OBJECT_TYPE)
//封装第一个参数.ClassLoader
dup();
push(0)
push((Type)null);
arrayStore(Type.getType(ClassLoader.class))
//封装第二个参数.增强类的类名
dup();
push(1)
push(tranClassName(classNAME));
arrayStore(Type.getType(String.class))
//封装第三个参数.方法名
dup();
push(2)
push(methodNAME);
arrayStore(Type.getType(String.class))
//封装第四个参数.方法描述
dup();
push(3)
push(methodDESC);
arrayStore(Type.getType(String.class))
//封装第五个参数,调用对象,
dup();
push(4)
loadThisOrPushNullIfIsStatic();
arrayStore(OBJECT_TYPE)
//封装第六个参数,方法参数
dup();
push(5)
loadArgArray();
arrayStore(Type.getType(Object[].class))
//执行com.freez.utils.clazz.WeaveSpy._onMethodEnter
invokeVirtual(METHOD_Type,METHOD_INVOKE_METHOD);
//将执行结果出栈
pop()
}
在解析方法内容每一行代码时,会调用visitLineNumber方法,本发明在visitLineNumber调用时植入增强接口com.freez.utils.clazz.WeaveSpy._invokeLine方法(植入也是通过ASM API,参考on MethodEnter的植入代码)
在解析方法结束行为时,会调用onMethodExit方法,本发明会判断增强类方法执行结果,如果未发生异常本发明在onMethodExit调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodE xit方法,如果发生了异常本发明在onMethodExit调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodThrowing方法(植入也是通过ASM API,参考onMethodEnter的植入代码)
到此本发明建立了类委托器com.freez.utils.clazz.ClassVisit orWeaver,
获取用户规定的要增强的类的原始字节码集合。
本发明核心类为com.freez.utils.clazz.Point,该类提供了public staticvoid open(Set<String>scanPackageSet)方法,用于打开本发明的能力,并且传入的参数定义了要扫描的java软件包名集合。
//对java包名为com.freez.mq.broker开头的所有类和子类进行扫描。
Set<String>sets=new HashSet<String>();
sets.add("com.freez.mq.broker");
//打开本软件功能,对符上述条件的类字节码增强,并且提供跟踪分析观察能力
Point.open(sets);
对所有的类进行扫描然后根据用户规定进行匹配,并且读取匹配上的类对应的字节码信息
{
//获取类加载器对象
ClassLoader appCL=Point.class.getClassLoader()
//获取该类加载器对应类.
Class<?>appCLClass=appCL.getClass().getSuperclass()
//获取类加载器的ucp字段(ucp记录了所有的类路径)
Field ucpField=appCLClass.getDeclaredField("ucp")
//获取类加载器的字段ucp(urlClassPath)的值
Object urlClassPath=ucpField.get(appCL)
//获取urlClassPath的getURLs获取类路径
Method getURLs=urlClassPath.getClass().getMethod("getURLs");
URL[]urls=(URL[])getURLs.invoke(urlClassPath)
for(URL url:urls)
{
//获取类路径中的class文件或者jar文件或者文件夹
File file=new File(URLDecoder.decode(url.getFile(),"UTF8"))
//递归处理这些文件,获取对应的类文件的原始字节码,
deep(file,file.getAbsolutePath(),whiteList)
}
}
此时,本发明获取了用户所有要增强的类的字节码集合
将上述原始字节码集合进行遍历,循环对每个字节码进行字节码增强.增强代码如下
//加载待增强类
Class<?>clazz=Class.forName(name)
//将使用字节码初始化类阅读器
ClassReader cr=new ClassReader(classDataArray)
//将使用类阅读器初始化类编写器
ClassWriter cw=new ClassWriter(cr,COMPUTE_FRAMES|COMPUTE_MAXS)
//通过类名和类编写器初始化类委托器;(该委托器用于对所有方法进行增强)
ClassVisitorWeaver classVisitorWeaver=new
ClassVisitorWeaver(clazz.getName(),cw)
//传入类委托器通过类阅读器的accept方法进行增强
cr.accept(classVisitorWeaver,EXPAND_FRAMES)
//获取增强后字节码
byte[]enhancedBytes=cw.toByteArray()
步骤4:兼容已存在的firstAgentlib,具体包括如下步骤:
步骤S4-1:为了区别本专利提供的Agentlib,定义其它的Agentlib为firstAgentlib,firstAgentlib对应的转换器为firstTrans former,判断是否存在firstAgentlib,如果存在,则获取转换器firstTransformer,具体包括先遍历代码命令行判断是否包含agentlib配置,如果包含则获取配置的对应java.lang.instrument.Instrumentation编码器,然后再获取编码器中的转换器firstTransforme r;
步骤S4-2:当使用专利定义的Agentlib进行字节码增强时,如果存在firstTransformer,先用firstTransformer进行字节码增强,再用专利定义的Agentlib进行二次增强。
本实施例中,判断是否存在其它的Agentlib,如果存在则获取转换器(firstTransformer)。
先遍历代码命令行判断是否包含agentlib配置.如果包含则获取配置的对应java.lang.instrument.Instrumentation(编码器),然后再获取编码器中的转换器(firstTransformer)。
//获取firstAgentlib的转换器管理对象
Field mTransformerManager_f=
Less.inst.getClass().getDeclaredField("mTransformerManager");
mTransformerManager_f.setAccessible(true);
Object mTransformerManager=mTransformerManager_f.get(Less.inst);
//转换管理器获取成功,则尝试获取转换器列表
if(mTransformerManager!=null)
{
//转换管理器获取成功,则尝试获取转换器列表
Ficld mTransformerList_f=
mTransformerManager.getClass().getDeclaredField("mTransformerList");
mTransformerList_f.setAccessible(true);
//转换器列表
Object[]mTransformerList=
(Object[])mTransformerList_f.get(mTransformerManager);
//
if(mTransformerList.length!=0)
{
Method method=
mTransformerList[0].getClass().getDeclaredMethod("transformer")
method.setAccessible(true);
//提取firstAgentlib中转化器
firstFileTransformer
(ClassFileTransformer)method.invoke(mTransformerList[0]);
log.info("窃取firstAgentlib的转换器firstFileTransformer成功");
}
}
此时,本发明获取了已经存在的转换器(firstTransformer),
当本发明使用自身的Agentlib进行字节码增强时,如果存在firstTransformer,先用firstTransformer进行字节码增强,再用本发明的Agentlib进行二次增强。
//增强后字节码
byte[]enhancedBytes=cw.toByteArray();
//把增强后的字节码enhancedBytes先用agentlib重新定义类,并且判断是否定义成功,
//如果失败,并且firstFileTransformer存在,则优先通过
firstFileTransformer转换.
if(!(power=Agent.redefineClasses(clazz,enhancedBytes))&&tryIt&&
Agent.firstFileTransformer!=null)
{
log.trace("尝试基于其firstFileTransformer转换器进行优先转换");
byte[]newdata=
Agent.firstFileTransformer.transform(Thread.currentThread()
.getContextClassLoader(),name,null,clazz.getProtectionDomain(),
data);
log.trace("转换成功!class length{}--->{}",data.length,
newdata.length);
//本发明获取firstFileTransformer增强后的字节码,再次传递给本agentlib二次增强
return power(name,newdata,false);
}
此时,本发明得到兼容了已存在的firstAgentlib的能。
本发明所述的一种Java进程运行期间动态代码栈分析观察方法,可以自动挂载agentlib并且兼容mac/linux/windwos,无需手动在启动命令中添加参数,提供额外jar包,提供了默认增强接口,可以通过任何一个slf4j日志提供商,就可以精确控制要关注的类对应代码运行细节,兼容其他agentlib,可以和第三方agentlib共存,使用便捷,本发明的所有实现都可以封装在一个freez-utils.jar包中,无需用户再引入其他任何软件包,将需要进行代码分析观察的项目引入freez-utils.jar,然后在启动类通过com.freez.utils.clazz.Point.open()方法打开本发明功能,就可以通过日志文件观察代码的运行详细轨迹、耗时、执行线程等信息。

Claims (5)

1.一种Java进程运行期间动态代码栈分析观察方法,其特征在于:包括如下步骤:
步骤1:解除VirtualMachine API和AttachProvider API的限制,根据操作系统的类型来建立匹配的AttachProvider,将Virtua lMachine API中的providers()方法逻辑的默认返回的com.sun.to ols.attach.spi.AttachProvider修改为com.freez.utils.attach.spi.AttachProvider,以允许外界访问;
通过调用attachProvider.attachVirtualMachine()方法获取vi rtualMachine
通过调用virtualMachine.loadAgent()方法来动态加载agentli b;
步骤2:获取Agentlib的动态生成与加载和得到定义的Agentli b获取编码器的能力,具体步骤包括通过Agentlib API支持完成字节码增强;通过Agentlib获取java.lang.instrument.Instrument ation编码器;通过java.lang.instrument.Instrumentation.rede fineClasses()方法在字节码增强后重新定义Class类;
步骤3:增强符合用户要求的类class,具体包括解除ASM API调用限制,建立增强接口com.freez.utils.clazz.WeaveSpy,增强接口接口com.freez.utils.clazz.WeaveSpy的默认行为是某个方法被执行到任意阶段时记录日志信息,建立类委托器com.freez.utils.clazz.ClassVisitorWeaver,用于字节码增强,获取用户规定的要增强的类的原始字节码集合,循环对每个字节码通过ASM API进行字节码增强;
步骤4:兼容已存在的firstAgentlib,具体包括如下步骤:
步骤S4-1:定义其它的Agentlib为firstAgentlib,firstAgen tlib对应的转换器为firstTransformer,判断是否存在其它的firs tAgentlib,如果存在,则获取其对应转换器firstTransformer,具体包括先遍历代码命令行判断是否包含agentlib配置,如果包含则获取配置的对应java.lang.instrument.Instrumentation编码器,然后再获取编码器中的转换器firstTransformer;
步骤S4-2:当使用定义的Agentlib进行字节码增强时,如果存在firstTransformer,先用firstTransformer进行字节码增强,再用定义的Agentlib中的transformer的进行二次增强。
2.如权利要求1所述的一种Java进程运行期间动态代码栈分析观察方法,其特征在于:在执行步骤1时,具体包括如下步骤:
步骤S1-1:通过java spi文件加载机制,找出针对不同操作系统的多个默认配置文件,将所有默认配置文件均放入同一个软件包中,生成配置软件包,并替换SPI文件中的默认提供者为com.freez.utils.attach.FreezAttachProvider;
步骤S1-2:在com.freez.utils.attach.FreezAttachProvide中,根据操作系统的类型来建立匹配的AttachProvider;
步骤S1-3:在VirtualMachine API中,先将复制jdk1.8中原始类com.sun.tools.attach.VirtualMachine复制一份到com.free z.utils.attach.spi.VirtualMachine,然后将providers()方法m默认返回从com.sun.tools.attach.spi.AttachProvider修改为com.freez.utils.attach.spi.AttachProvider,以允许外界访问。
3.如权利要求1所述的一种Java进程运行期间动态代码栈分析观察方法,其特征在于:在执行步骤2时,具体步骤如下:
步骤S2-1:将配置文件和编译器接类com.freez.utils.clazz.Agent$Less放入temp.jar中,然后在java进程启动时,将temp.ja r重新命名为随机数.jar,随机数.jar即为agentlib,最后将随机数.jar加载到virtualMachine中;
步骤S2-2:在随机数.jar加载后,即agentlib加载成功后,JV M会将java.lang.instrument.Instrumentation编码器作为参数传入com.freez.utils.clazz.Agent$Less.agentmain()方法中,从而获得java.lang.instrument.Instrumentation编码器,拥有重新定义类的能力。
4.如权利要求1所述的一种Java进程运行期间动态代码栈分析观察方法,其特征在于:在执行步骤3时,具体包括如下步骤:
步骤S3-1:解除ASM API调用限制;具体包括找出jdk的jdk.internal.org.objectweb.asm软件包中的AMS API,将openjdk的对应源码复制到com.freez.utils.objectweb.asm包内,以得到ASM API调用权限;
步骤S3-2:建立增强接口com.freez.utils.clazz.WeaveSpy,接口对目标类所有方法进行增强,默认效果是为默认目标方法执行周期输出日志记录,com.freez.utils.clazz.WeaveSpy接口将被植入到用户待增强的类中;
在增强类所被调用方法进入时调用com.freez.utils.clazz.Weav eSpy._onMethodEnter()方法;
在增强类所被调用方法结束调用com.freez.utils.clazz.WeaveSpy._onMethodExit()方法;
在增强类所被调用方法运行中com.freez.utils.clazz.WeaveSpy._invokeLine()方法;
在增强类所被调用方法发生异常时调用com.freez.utils.clazz.WeaveSpy._onMethodThrowing()方法;
步骤S3-3:建立类委托器com.freez.utils.clazz.ClassVisito rWeaver,用于字节码增强,具体包括在类委托器解析某个原始字节码中的任何方法时,调用visitMethod方法进行方法解析;在解析方法开始行为时调用onMethodEnter方法,在onMethodEnter调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodEnter方法;
在解析方法内容每一行代码时,调用visitLineNumber方法,在visitLineNumber调用时通过ASM API植入增强接口com.freez.utils.clazz.WeaveSpy._invokeLine方法;
在解析方法结束行为时,调用onMethodExit方法,判断增强类方法执行结果,如果未发生异常在onMethodExit调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodExit方法,如果发生了异常在onMethodExit调用时植入增强接口com.freez.utils.clazz.WeaveSpy._onMethodThrowing方法;
步骤S3-4:获取用户规定的要增强的类的原始字节码集合;
步骤S3-5:遍历原始字节码集合,循环对每个字节码通过ASM API进行字节码增强。
5.如权利要求4所述的一种Java进程运行期间动态代码栈分析观察方法,其特征在于:在执行步骤S3-4时,具体包括如下步骤:
步骤S3-4-1:定义核心类为com.freez.utils.clazz.Point,核心类提供了publicstatic void open(Set<String>scanPackageSe t)方法,核心类传入的参数定义了要扫描的java软件包名集合,假设用户调用open传入包名为“com.freez.mq.broker”,对java包名为com.freez.mq.broker开头的所有类和子类进行扫描;
步骤S3-4-2:通过com.freez.utils.clazz.Point.open()方法打开本软件功能,对符上述条件的类字节码增强,并且提供跟踪分析观察能力;
步骤S3-4-3:对所有的类进行扫描然后根据用户规定进行匹配,并且读取匹配上的类对应的字节码信息,获取类加载器对象;
步骤S3-4-4:获取类加载器对应类;
步骤S3-4-5:获取类加载器的ucp字段,ucp记录了所有的类路径;
步骤S3-4-6:获取类加载器的字段ucp(urlClassPath)的值;
步骤S3-4-7:获取urlClassPath的getURLs获取类路径;
步骤S3-4-8:获取类路径中的class文件或者jar文件或者文件夹;
步骤S3-4-9:递归处理这些文件,获取对应的类文件的原始字节码。
CN202310502766.9A 2023-05-06 2023-05-06 一种Java进程运行期间动态代码栈分析观察方法 Pending CN116541282A (zh)

Priority Applications (1)

Application Number Priority Date Filing Date Title
CN202310502766.9A CN116541282A (zh) 2023-05-06 2023-05-06 一种Java进程运行期间动态代码栈分析观察方法

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
CN202310502766.9A CN116541282A (zh) 2023-05-06 2023-05-06 一种Java进程运行期间动态代码栈分析观察方法

Publications (1)

Publication Number Publication Date
CN116541282A true CN116541282A (zh) 2023-08-04

Family

ID=87442998

Family Applications (1)

Application Number Title Priority Date Filing Date
CN202310502766.9A Pending CN116541282A (zh) 2023-05-06 2023-05-06 一种Java进程运行期间动态代码栈分析观察方法

Country Status (1)

Country Link
CN (1) CN116541282A (zh)

Similar Documents

Publication Publication Date Title
Czajkowski Application isolation in the Java virtual machine
US6662362B1 (en) Method and system for improving performance of applications that employ a cross-language interface
US7191441B2 (en) Method and apparatus for suspending a software virtual machine
Douence et al. An expressive aspect language for system applications with Arachne
US8359582B2 (en) Compiling and inserting code snippets at runtime
JP4880121B2 (ja) バーチャル・マシン環境でネイティブ・コードを変換し、実行する方法および装置
Engel et al. Supporting autonomic computing functionality via dynamic operating system kernel aspects
US20040230958A1 (en) Compiler and software product for compiling intermediate language bytecodes into Java bytecodes
Welch et al. {Kava--Using}{Byte-Code} Rewriting to Add Behavioral Reflection to Java
Feng et al. An open framework for foundational proof-carrying code
Ogawa et al. OpenJIT: An open-ended, reflective JIT compiler framework for Java
Briand et al. Instrumenting contracts with aspect-oriented programming to increase observability and support debugging
CN116541282A (zh) 一种Java进程运行期间动态代码栈分析观察方法
US7207036B2 (en) Preprocessing of interfaces to allow fast call through
EP1662398B1 (en) Apparatus and method for observing runtime behavior of an application program
CA3118057A1 (en) Selective substitution of legacy load module programs with classes for execution in a java virtual machine
JP2003330736A (ja) 不正リソース利用防止システム及びその方法並びにプログラム
Bernardeschi et al. Checking secure information flow in java bytecode by code transformation and standard bytecode verification
EP1136910A2 (en) A method of compiling code in an object oriented programming language
Hlopko et al. On the integration of Smalltalk and Java: practical experience with STX: LIBJAVA
Binder Secure and reliable java-based middleware-challenges and solutions
Wood et al. A novel technique for control flow obfuscation in JVM applications using InvokeDynamic with native bootstrapping
CN115390913B (zh) 零代码侵入的日志监控方法、装置、电子设备及存储介质
WO2002093382A2 (en) System and method for automated assertion acquisition in a java compatibility testing
Courbot et al. Application-driven customization of an embedded java virtual machine

Legal Events

Date Code Title Description
PB01 Publication
PB01 Publication
SE01 Entry into force of request for substantive examination
SE01 Entry into force of request for substantive examination