BTrace 动态追踪技术

直接操作字节码

Java是软件开发人员能读懂的语言,class字节码是JVM能读懂的语言,class字节码最终会被JVM解释成机器能读懂的语言。无论哪种语言,都是人创造的。所以,理论上(实际上也确实如此)人能读懂上述任何一种语言,既然能读懂,自然能修改。只要我们愿意,我们完全可以跳过Java编译器,直接写字节码文件,只不过这并不符合时代的发展罢了,毕竟高级语言设计之始就是为我们人类所服务,其开发效率也比机器语言高很多。

对于人类来说,字节码文件的可读性远远没有Java代码高。尽管如此,还是有一些杰出的程序员们创造出了可以用来直接编辑字节码的框架,提供接口可以让我们方便地操作字节码文件,进行注入修改类的方法,动态创造一个新的类等等操作。其中最著名的框架应该就是ASM了,cglib、Spring等框架中对于字节码的操作就建立在ASM之上。

我们都知道,Spring的AOP是基于动态代理实现的,Spring会在运行时动态创建代理类,代理类中引用被代理类,在被代理的方法执行前后进行一些神秘的操作。那么,Spring是怎么在运行时创建代理类的呢?动态代理的美妙之处,就在于我们不必手动为每个需要被代理的类写代理类代码,Spring在运行时会根据需要动态地创造出一个类,这里创造的过程并非通过字符串写Java文件,然后编译成class文件,然后加载。Spring会直接“创造”一个class文件,然后加载,创造class文件的工具,就是ASM了。

到这里,我们知道了用ASM框架直接操作class文件,在类中加一段打印日志的代码,然后调用retransformClasses就可以了。

BTrace

截止到目前,我们都是停留在理论描述的层面。那么如何进行实现呢?先来看几个问题:

  1. 在我们的工程中,谁来做这个寻找字节码,修改字节码,然后reTransform的动作呢?我们并非先知,不可能知道未来有没有可能遇到文章开头的这种问题。考虑到性价比,我们也不可能在每个工程中都开发一段专门做这些修改字节码、重新加载字节码的代码。
  2. 如果JVM不在本地,在远程呢?
  3. 如果连ASM都不会用呢?能不能更通用一些,更“傻瓜”一些。

幸运的是,因为有BTrace的存在,我们不必自己写一套这样的工具了。什么是BTrace呢?BTrace已经开源,项目描述极其简短:

A safe, dynamic tracing tool for the Java platform.

BTrace是基于Java语言的一个安全的、可提供动态追踪服务的工具。BTrace基于ASM、Java Attach Api、Instruments开发,为用户提供了很多注解。依靠这些注解,我们可以编写BTrace脚本(简单的Java代码)达到我们想要的效果,而不必深陷于ASM对字节码的操作中不可自拔。

看BTrace官方提供的一个简单例子:拦截所有java.io包中所有类中以read开头的方法,打印类名、方法名和参数名。当程序IO负载比较高的时候,就可以从输出的信息中看到是哪些类所引起,是不是很方便?


package com.sun.btrace.samples;

import com.sun.btrace.annotations.*;
import com.sun.btrace.AnyType;
import static com.sun.btrace.BTraceUtils.*;

/**
 * This sample demonstrates regular expression
 * probe matching and getting input arguments
 * as an array - so that any overload variant
 * can be traced in "one place". This example
 * traces any "readXX" method on any class in
 * java.io package. Probed class, method and arg
 * array is printed in the action.
 */
@BTrace public class ArgArray {
    @OnMethod(
        clazz="/java\\.io\\..*/",
        method="/read.*/"
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
        println(pcn);
        println(pmn);
        printArray(args);
    }
}

再来看另一个例子:每隔2秒打印截止到当前创建过的线程数。


package com.sun.btrace.samples;

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.Export;

/**
 * This sample creates a jvmstat counter and
 * increments it everytime Thread.start() is
 * called. This thread count may be accessed
 * from outside the process. The @Export annotated
 * fields are mapped to jvmstat counters. The counter
 * name is "btrace." + <className> + "." + <fieldName>
 */
@BTrace public class ThreadCounter {

    // create a jvmstat counter using @Export
    @Export private static long count;

    @OnMethod(
        clazz="java.lang.Thread",
        method="start"
    )
    public static void onnewThread(@Self Thread t) {
        // updating counter is easy. Just assign to
        // the static field!
        count++;
    }

    @OnTimer(2000)
    public static void ontimer() {
        // we can access counter as "count" as well
        // as from jvmstat counter directly.
        println(count);
        // or equivalently ...
        println(Counters.perfLong("btrace.com.sun.btrace.samples.ThreadCounter.count"));
    }
}

看了上面的用法是不是有所启发?忍不住冒出来许多想法。比如查看HashMap什么时候会触发rehash,以及此时容器中有多少元素等等。

有了BTrace,文章开头的问题可以得到完美的解决。至于BTrace具体有哪些功能,脚本怎么写,这些Git上BTrace工程中有大量的说明和举例,网上介绍BTrace用法的文章更是恒河沙数,这里就不再赘述了。

我们明白了原理,又有好用的工具支持,剩下的就是发挥我们的创造力了,只需在合适的场景下合理地进行使用即可。

既然BTrace能解决上面我们提到的所有问题,那么BTrace的架构是怎样的呢?

BTrace主要有下面几个模块:

  1. BTrace脚本:利用BTrace定义的注解,我们可以很方便地根据需要进行脚本的开发。
  2. Compiler:将BTrace脚本编译成BTrace class文件。
  3. Client:将class文件发送到Agent。
  4. Agent:基于Java的Attach Api,Agent可以动态附着到一个运行的JVM上,然后开启一个BTrace Server,接收client发过来的BTrace脚本;解析脚本,然后根据脚本中的规则找到要修改的类;修改字节码后,调用Java Instrument的reTransform接口,完成对对象行为的修改并使之生效。

整个BTrace的架构大致如下:

BTrace工作流程

BTrace最终借Instruments实现class的替换。如上文所说,出于安全考虑,Instruments在使用上存在诸多的限制,BTrace也不例外。BTrace对JVM来说是“只读的”,因此BTrace脚本的限制如下:

  1. 不允许创建对象
  2. 不允许创建数组
  3. 不允许抛异常
  4. 不允许catch异常
  5. 不允许随意调用其他对象或者类的方法,只允许调用com.sun.btrace.BTraceUtils中提供的静态方法(一些数据处理和信息输出工具)
  6. 不允许改变类的属性
  7. 不允许有成员变量和方法,只允许存在static public void方法
  8. 不允许有内部类、嵌套类
  9. 不允许有同步方法和同步块
  10. 不允许有循环
  11. 不允许随意继承其他类(当然,java.lang.Object除外)
  12. 不允许实现接口
  13. 不允许使用assert
  14. 不允许使用Class对象

如此多的限制,其实可以理解。BTrace要做的是,虽然修改了字节码,但是除了输出需要的信息外,对整个程序的正常运行并没有影响。

原文:https://tech.meituan.com/2019/02/28/java-dynamic-trace.html

原文地址:https://www.cnblogs.com/yx88/p/11291655.html

时间: 2025-01-18 04:58:04

BTrace 动态追踪技术的相关文章

动态追踪技术(中) - Dtrace、SystemTap、火焰图

http://openresty.org/cn/presentations.html http://weibo.com/agentzh?is_all=1 http://openresty.org/posts/dynamic-tracing/ 动态追踪技术(中) - Dtrace.SystemTap.火焰图 原创 2016-05-06 章亦春 MacTalk 动态追踪技术中篇,关于 DTrace.SystemTap 和 火焰图的那点事. DTrace 与 SystemTap 说到动态追踪就不能不提

2019国内某知名科技公司技术资料

2019年过去了,国内某知名品台分享了一波他们2019年的内部的技术资料.十余年的技术积累,是他们团队继续成长的有力后盾.过去的一年间, 技术博客继续践行「从内部提炼不少优质内容,分享出来与业界同仁一起学习交流.其中前端领 域发布数十篇优质文章,包括动态化.代码质量及安全.App 流程管理及实践.前端 基本功等. [前端篇]:http://dpurl.cn/Xy6IOU4 Java 魔法类:Unsafe 应用解析 Java 动态追踪技术探究 字节码增强技术探索 JVM CPU Profiler

动态追踪怎么用?(上)

动态追踪技术原因 当碰到内核线程的资源使用异常时,很多常用的进程级性能工具,并不能直接用到内核线程上.这时,我们就可以使用内核自带的 perf 来观察它们的行为,找出热点函数,进一步定位性能瓶颈.不过,perf 产生的汇总报告并不直观,所以我通常也推荐用火焰图来协助排查. 其实,使用 perf 对系统内核线程进行分析时,内核线程依然还在正常运行中,所以这种方法也被称为动态追踪技术. 动态追踪技术,通过探针机制,来采集内核或者应用程序的运行信息,从而可以不用修改内核和应用程序的代码,就获得丰富的信

电子书 Linux Perf Master.pdf

Linux Perf Master About Application Performance Assessment, diagnosis, monitoring and analysis method. Apply to Linux, Solaris, Mac OS and Windows. 作者:RiboseYim 工程师.开源粉.深度阅读患者.高效运维社区2016年度金牌作者 特色章节 How Linux Works基于Linux单机的负载评估动态追踪技术:DTrace动态追踪案例:str

BPF漫谈

源起 最近看到国内两篇文章[1][2]先后翻译了就职于Netflix的性能分析大牛Brendan Gregg于2017年7月31日写的<Golang bcc/BPF Function Tracing>[3],这迅速引起了我的兴趣,2016年时我曾在做MQTT服务器端开发时便意识到软件调试及动态追踪技术的重要性,其间研究春哥(章亦春 agentzh)的<动态追踪技术漫谈>[4]时,文中提及"最近几年 Linux 的主线开发者们,把原来用于防火墙的 netfilter 里所使

cpu很高,但是看不到是哪个应用或进程

碰到常规问题无法解释的 CPU 使用率情况时,首先要想到有可能是短时应用导致的问题,比如有可能是下面这两种情况. 第一,应用里直接调用了其他二进制程序,这些程序通常运行时间比较短,通过 top 等工具也不容易发现. 第二,应用本身在不停地崩溃重启,而启动过程的资源初始化,很可能会占用相当多的 CPU. 对于这类进程,我们可以用 pstree 或者 execsnoop 找到它们的父进程,再从父进程所在的应用入手,排查问题的根源. execsnoop 所用的 ftrace 是一种常用的动态追踪技术,

JVM笔记——技术点汇总

目录 · 初步认识 · Java里程碑(关键部分) · 理解虚拟机 · Java虚拟机种类 · Java语言规范 · Java虚拟机规范 · 基本结构 · Java堆(Heap) · Java栈(Stacks) · 方法区(Method Area) · 直接内存(Direct Memory) · 本地方法栈(Native Method Stacks) · 常用参数 · 设置参数 · 查看参数 · 跟踪垃圾回收 · 跟踪类加载/卸载 · 设置初始堆和最大堆 · 设置堆分布 · 处理堆溢出 · 配置

btrace一些你不知道的事(源码入手)

背景 周五下班回家,在公司班车上觉得无聊,看了下btrace的源码(自己反编译). 一些关于btrace的基本内容,可以看下我早起的一篇记录:btrace记忆 上一篇主要介绍的是btrace的一些基本使用以及api,这里我想从btrace源码本身进行下介绍.至于btrace的优势,能用来干些什么,自己上他的官网看下或者google一下,花个半小时就能明白了. 至于为什么会去反编译查看btrace源码,主要是会在部门整个关于btrace的分享.同时btrace的相关技术文档缺乏,javadoc很多

全景VR视频游戏外包公司:技术分享使用U3D+CB制作VR游戏

随着Oculus宣布1月6日开启预售,2016年很可能成为VR游戏元年,但很多的调研显示,手游设备才是市场增长的关键,SuperData发布的报告显示,2016年全球VR游戏市场规模预计在51亿美元左右,消费者设备安装量在3890万左右,凭借价格门槛的优势,以谷歌Cardboard和三星Gear VR为代表的移动VR设备很大可能成为用户量最大的设备,可能占据2016年虚拟现实设备安装量71%的份额,达到2700万. 我们的老朋友Ray在自己的博客中展示了如何用Unity和Cardboard把一款