CN116541282A - 一种Java进程运行期间动态代码栈分析观察方法 - Google Patents
一种Java进程运行期间动态代码栈分析观察方法 Download PDFInfo
- 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
Links
- 238000000034 method Methods 0.000 title claims abstract description 200
- 238000004458 analytical method Methods 0.000 title claims abstract description 20
- 238000011112 process operation Methods 0.000 title abstract description 5
- 230000002708 enhancing effect Effects 0.000 claims abstract description 9
- 239000008186 active pharmaceutical agent Substances 0.000 claims description 50
- 230000008569 process Effects 0.000 claims description 22
- 239000003795 chemical substances by application Substances 0.000 claims description 13
- 230000002159 abnormal effect Effects 0.000 claims description 6
- 230000003068 static effect Effects 0.000 claims description 5
- 239000011800 void material Substances 0.000 claims description 5
- 230000007246 mechanism Effects 0.000 claims description 4
- 238000004454 trace mineral analysis Methods 0.000 claims description 3
- 230000000694 effects Effects 0.000 claims description 2
- 238000012545 processing Methods 0.000 claims description 2
- 230000007935 neutral effect Effects 0.000 claims 3
- 230000000737 periodic effect Effects 0.000 claims 1
- 230000010076 replication Effects 0.000 claims 1
- 230000006870 function Effects 0.000 description 7
- 238000006243 chemical reaction Methods 0.000 description 5
- 238000002513 implantation Methods 0.000 description 5
- 230000005856 abnormality Effects 0.000 description 3
- 230000007547 defect Effects 0.000 description 2
- 230000004048 modification Effects 0.000 description 2
- 238000012986 modification Methods 0.000 description 2
- 238000012544 monitoring process Methods 0.000 description 2
- 238000012360 testing method Methods 0.000 description 2
- 201000008217 Aggressive systemic mastocytosis Diseases 0.000 description 1
- BLRPTPMANUNPDV-UHFFFAOYSA-N Silane Chemical compound [SiH4] BLRPTPMANUNPDV-UHFFFAOYSA-N 0.000 description 1
- 230000007812 deficiency Effects 0.000 description 1
- 238000010586 diagram Methods 0.000 description 1
- 238000005538 encapsulation Methods 0.000 description 1
- 238000000605 extraction Methods 0.000 description 1
- 239000007943 implant Substances 0.000 description 1
- 230000008676 import Effects 0.000 description 1
- 238000002715 modification method Methods 0.000 description 1
- 230000011664 signaling Effects 0.000 description 1
- 229910000077 silane Inorganic materials 0.000 description 1
Classifications
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F11/00—Error detection; Error correction; Monitoring
- G06F11/36—Preventing errors by testing or debugging software
- G06F11/362—Software debugging
- G06F11/3636—Software debugging by tracing the execution of the program
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F11/00—Error detection; Error correction; Monitoring
- G06F11/36—Preventing errors by testing or debugging software
- G06F11/3604—Software analysis for verifying properties of programs
- G06F11/3612—Software analysis for verifying properties of programs by runtime analysis
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F11/00—Error detection; Error correction; Monitoring
- G06F11/36—Preventing errors by testing or debugging software
- G06F11/362—Software debugging
- G06F11/3624—Software debugging by performing operations on the source code, e.g. via a compiler
-
- Y—GENERAL 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
- Y02—TECHNOLOGIES OR APPLICATIONS FOR MITIGATION OR ADAPTATION AGAINST CLIMATE CHANGE
- Y02D—CLIMATE 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/00—Energy 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 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:递归处理这些文件,获取对应的类文件的原始字节码。
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) |
-
2023
- 2023-05-06 CN CN202310502766.9A patent/CN116541282A/zh active Pending
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 |