简介
1、Btrace是什么?
在生产环境中经常遇到格式各样的问题,如OOM或者莫名其妙的进程死掉。一般情况下是通过修改程序,添加打印日志;然后重新发布程序来完成。然而,这不仅麻烦,而且带来很多不可控的因素。有没有一种方式,在不修改原有运行程序的情况下获取运行时的数据信息呢?如方法参数、返回值、全局变量、堆栈信息等。Btrace就是这样一个工具,它可以在不修改原有代码的情况下动态地追踪java运行程序,通过hotswap技术,动态将跟踪字节码注入到运行类中,对运行代码侵入较小,对性能上的影响可以忽略不计。
2、Btrace的使用限制?
由于Btrace会把脚本逻辑直接侵入到运行的代码中,所以在使用上做很多限制:
- 不能创建对象
- 不能使用数组
- 不能抛出或捕获异常
- 不能使用循环
- 不能使用synchronized关键字
- 属性和方法必须使用static修饰
注意:BTrace植入过的代码,会一直在,直到应用重启为止。所以即使Btrace退出了,业务函数每次执行时都会执行Btrace植入的代码
3、Btrace能做什么?
1、接口性能变慢,分析每个方法的耗时情况;
2、当在Map中插入大量数据,分析其扩容情况;
3、分析哪个方法调用了System.gc(),调用栈如何;
4、执行某个方法抛出异常时,分析运行时参数
安装
1、安装包:
链接:https://pan.baidu.com/s/1MnL6wXRy0y5l573jlgo2BQ
提取码:kh9f
2、配置环境变量
linux下安装设置环境变量:
vi /etc/profile
BTRACE_HOME=/root/btrace/btrace-bin-1.3.11.3
PATH=$PATH:$BTRACE_HOME/bin
export PATH JAVA_HOME
source /etc/profile
window下安装设置环境变量:
BTRACE_HOME E:\btrace-1.3.11.3
%BTRACE_HOME%\bin;
设置完后:验证是否安装成功
btrace --version
BTrace v.1.3.11.3 (20181217)
使用
1、获取方法的参数值
在工程里新建一个简单的controller,用于演示如何利用BTrace脚本来实时获取方法的参数值:
@Controller
public class BtraceController {
@ResponseBody
@GetMapping("/hello")
public String hello(String name){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello "+name;
}
}
依赖:
<!--btrace start-->
<dependency>
<groupId>com.sun.btrace</groupId>
<artifactId>btrace-agent</artifactId>
<version>1.3.11</version>
<type>jar</type>
<scope>system</scope>
<systemPath>E:/btrace-1.3.11.3/build/btrace-agent.jar</systemPath>
</dependency>
<dependency>
<groupId>com.sun.btrace</groupId>
<artifactId>btrace-boot</artifactId>
<version>1.3.11</version>
<type>jar</type>
<scope>system</scope>
<systemPath>E:/btrace-1.3.11.3/build/btrace-boot.jar</systemPath>
</dependency>
<dependency>
<groupId>com.sun.btrace</groupId>
<artifactId>btrace-client</artifactId>
<version>1.3.11</version>
<type>jar</type>
<scope>system</scope>
<systemPath>E:/btrace-1.3.11.3/build/btrace-client.jar</systemPath>
</dependency>
<!--btrace end-->
clean install一下,部署到服务器里,模仿生产环境。
编写Btrace脚本:
import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
// 注明这是一个BTrace脚本
@BTrace
public class PrintArgSimple {
// 指定需要拦截的方法
@OnMethod(
// 类的路径
clazz="com.example.designpattern.controller.BtraceController",
// 方法名
method="hello",
// 在什么时候进行拦截
location=@Location(Kind.ENTRY)
)
// 被拦截的类名;被拦截的方法名;被拦截的方法的参数值
public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
// 打印数组
BTraceUtils.printArray(args);
// 打印行
BTraceUtils.println(pcn+","+pmn);
BTraceUtils.println();
}
}
把脚本放到服务器里执行:
[root@localhost btrace]# jps -l
21557 /root/project/designpattern-0.0.1-SNAPSHOT.jar
21621 sun.tools.jps.Jps
[root@localhost btrace]# btrace 21557 PrintArgSimple.java
[zhangsan, ]
com.example.designpattern.controller.BtraceController,hello
访问接口:http://192.168.169.129:8019/hello?name=zhangsan
2、拦截构造函数、同名函数
2.1 拦截构造函数
添加controller,重新部署
@ResponseBody
@RequestMapping(value = "/constructor")
public Teacher constructor(Teacher teacher){
return teacher;
}
public class Teacher {
private Integer id;
private Integer age;
private String name;
public Teacher(Integer id, Integer age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
btrace脚本:
改类的相对路径
@BTrace
public class PrintConstructor {
@OnMethod(
//拦截的类
clazz="com.example.designpattern.controller.Teacher",
//指定拦截构造函数
method="<init>"
)
public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
//类名
BTraceUtils.println("ClassName: " + pcn);
//方法名
BTraceUtils.println("MethodName: " + pmn);
//参数名
BTraceUtils.printArray(args);
BTraceUtils.println();
}
}
启动btrace脚本并访问接口:
[root@localhost btrace]# jps -l
22729 /root/project/designpattern-0.0.1-SNAPSHOT.jar
22794 sun.tools.jps.Jps[root@localhost btrace]# btrace 22729 PrintConstructor.java
ClassName: com.example.designpattern.controller.Teacher
MethodName: <init>
[1, 14, zhangsan, ]
2.1 拦截同名方法
controller:
/**
* 测试拦截同名函数
* @param name
* @return
*/
@ResponseBody
@RequestMapping("/same1")
public String same(@RequestParam("name")String name) {
return "hello,"+name;
}
@ResponseBody
@RequestMapping("/same2")
public String same(@RequestParam("name")String name,@RequestParam("id")int id) {
return "hello,"+name+","+id;
}
btrace脚本:
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
@BTrace
public class PrintSame {
@OnMethod(
clazz = "com.example.designpattern.controller.BtraceController",
method = "same"
)
public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, String name) {
BTraceUtils.println("ClassName: " + pcn);
BTraceUtils.println("MethodName: " + pmn);
BTraceUtils.println("name: " + name);
BTraceUtils.println();
}
@OnMethod(
//拦截的类
clazz = "com.example.designpattern.controller.BtraceController",
//方法
method = "same"
)
public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, String name,int id ) {
BTraceUtils.println("ClassName: " + pcn);
BTraceUtils.println("MethodName: " + pmn);
BTraceUtils.println("id: " + id);
BTraceUtils.println("name: " + name);
BTraceUtils.println();
}
}
从以上的BTrace脚本中可以看到,其实拦截重载方法的话,只需要在BTrace脚本的方法中声明与之对应的参数即可,顺序也要一致。
测试:
[root@localhost btrace]# btrace 24450 PrintSame.java
ClassName: com.example.designpattern.controller.BtraceController
MethodName: same
name: aa
ClassName: com.example.designpattern.controller.BtraceController
MethodName: same
id: 123
name: aa
3、拦截返回值、异常、行号
Kind.ENTRY // 入口拦截,默认值
Kind.RETURN // 拦截返回值
Kind.THROW // 发生异常时拦截
Kind.LINE // 拦截某一行
1、拦截返回值
bstace脚本:
@BTrace
public class PrintReturn {
@OnMethod(
clazz="com.example.designpattern.controller.BtraceController",
method="hello",
location=@Location(Kind.RETURN) // 拦截返回值
)
public static void anyRead(@ProbeClassName String pcn,
@ProbeMethodName String pmn,
@Return AnyType result )
{
BTraceUtils.println("ClassName: " + pcn);
BTraceUtils.println("MethodName: " + pmn);
BTraceUtils.println("ResultValue: " + result);
BTraceUtils.println();
}
}
访问hello接口
[root@localhost btrace]# jps -l
20460 /root/project/designpattern-0.0.1-SNAPSHOT.jar
20492 sun.tools.jps.Jps
[root@localhost btrace]# btrace 20460 PrintReturn.java
ClassName: com.example.designpattern.controller.BtraceController
MethodName: hello
ResultValue: hello 23
2、异常
已经工作了的小伙伴们,应该会遇到一个常见的现象。那就是在很多旧项目的遗留代码中,总是能看到很多不妥的处理异常的方式。例如经常能看到把异常使用try-catch包起来,但是又不打印异常堆栈,也不抛出去。结果就是把异常隐藏了起来,没能让异常终止逻辑,导致了代码继续执行,还无法定位问题的所在。就如同以下这个方法一样:
/**
* 测试拦截时机--异常
* @return
*/
@RequestMapping("exception")
public String exception(){
try{
System.out.println("start....");
System.out.println(1/0);
System.out.println("end....");
}
catch(Exception e)
{
}
return "success";
}
我们访问以上这个接口,会看到返回的值是正确的,但是也会发现有部分逻辑没被执行,查看日志或控制台输出也没有异常信息。当出现这种情况,就真的两眼一抹黑了,根本无法排查问题(这里作为演示代码很简单,但是实际的项目代码可不是这样)。
还好我们现在知道了BTrace这样的调试工具,那么就可以利用BTrace脚本来拦截异常并打印异常堆栈,从而定位问题。
新建一个BTrace脚本,代码如下,这是我直接从官方示例里拿来的代码:
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.Self;
import com.sun.btrace.annotations.TLS;
@BTrace
public class PrintOnThrow {
// store current exception in a thread local
// variable (@TLS annotation). Note that we can't
// store it in a global variable!
@TLS
static Throwable currentException;
// introduce probe into every constructor of java.lang.Throwable
// class and store "this" in the thread local variable.
@OnMethod(
clazz="java.lang.Throwable",
method="<init>"
)
public static void onthrow(@Self Throwable self) {//new Throwable()
currentException = self;
}
@OnMethod(
clazz="java.lang.Throwable",
method="<init>"
)
public static void onthrow1(@Self Throwable self, String s) {//new Throwable(String msg)
currentException = self;
}
@OnMethod(
clazz="java.lang.Throwable",
method="<init>"
)
public static void onthrow1(@Self Throwable self, String s, Throwable cause) {//new Throwable(String msg, Throwable cause)
currentException = self;
}
@OnMethod(
clazz="java.lang.Throwable",
method="<init>"
)
public static void onthrow2(@Self Throwable self, Throwable cause) {//new Throwable(Throwable cause)
currentException = self;
}
// when any constructor of java.lang.Throwable returns
// print the currentException's stack trace.
@OnMethod(
clazz="java.lang.Throwable",
method="<init>",
location=@Location(Kind.RETURN)
)
public static void onthrowreturn() {
if (currentException != null) {
BTraceUtils.Threads.jstack(currentException);
BTraceUtils.println("=====================");
currentException = null;
}
}
}
[root@localhost btrace]# btrace 20871 PrintOnThrow.java >1.log
3、行号
我们还可以使用BTrace拦截某一行代码,以此判断该行代码是否有被执行。在btrace包下新建一个BTrace脚本,代码如下:
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
@BTrace
public class PrintLine {
@OnMethod(
clazz="org.zero01.monitor_tuning.controller.BTraceController",
method="exception",
location=@Location(value=Kind.LINE, line=43) // 拦截第43行
)
public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {
BTraceUtils.println("ClassName: " + pcn);
BTraceUtils.println("MethodName: " + pmn);
BTraceUtils.println("line: " + line);
BTraceUtils.println();
}
}
注意:line=-1 打印所有行号
在命令行里运行该脚本,访问相应的接口后,输出的信息如下:
E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>btrace 14952 PrintLine.java
ClassName: org.zero01.monitor_tuning.controller.BTraceController
MethodName: exception
line: 43
如果没有任何输出的话,就代表那一行没有被执行到,所以没被拦截。这种拦截某一行的方式,不适用于判断是否有异常,只能单纯用于判断某一行是否被执行了。
5、拦截复杂参数、环境变量、正则匹配拦截
5.1、拦截复杂参数
拦截复杂参数就是拦截实体对象类型的参数,在 BTraceController 类里,增加如下方法:
@RequestMapping("arg2")
public Teacher arg2(Teacher teacher) {
return teacher;
}
使用BTrace拦截复杂参数,需要使用反射的方式进行拦截,也就是需要传递包名+属性名。在btrace包下新建一个BTrace脚本,代码如下:
import java.lang.reflect.Field;
import com.example.designpattern.controller.Teacher;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
@BTrace
public class PrintArgComplex {
@OnMethod(
clazz="com.example.designpattern.controller.BtraceController",
method="arg2",
location=@Location(Kind.ENTRY)
)
public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, Teacher teacher) {
BTraceUtils.printFields(teacher);
Field filed2 = BTraceUtils.field("com.example.designpattern.controller.Teacher", "name");
BTraceUtils.println(BTraceUtils.get(filed2, teacher));
BTraceUtils.println(pcn+","+pmn);
BTraceUtils.println();
}
}
在命令行里运行该脚本,访问相应的接口后,输出的信息如下:
F:\ideaWebProject\designpattern\src\main\java\com\example\designpattern\btrace>btrace -cp "F:\ideaWebProject\designpattern\target\classes" 1668 PrintArgComplex.java
{id=1, age=22, name=aaa, }
aaa
com.example.designpattern.controller.BtraceController,arg2
注:这里使用到了一个 -cp 参数,该参数表示指定一个classpath路径
如果是linux下,在执行btrace脚本的时候,需要将User对象加入到classpath里面来。如果某个对象是jdk原生的对象,则无需要这个操作。因此,需要将jdk原生类型之外的类型加入到classpath里面来。
adog@E531:chapter4$ btrace -cp /home/adog/IdeaProjects/monitor_tuning0822/target/classes 7571 PrintArgComplex.java
5.2、环境变量
通过编写BTrace脚本我们可以打印出JVM的信息以及环境变量等,就类似于jinfo命令一样。代码示例:
@BTrace
public class PrintJinfo {
static {
// 打印系统属性
BTraceUtils.println("System Properties:");
BTraceUtils.printProperties();
// 打印JVM参数
BTraceUtils.println("VM Flags:");
BTraceUtils.printVmArguments();
// 打印环境变量
BTraceUtils.println("OS Enviroment:");
BTraceUtils.printEnv();
// 退出脚本
BTraceUtils.exit(0);
}
}
5.3、正则匹配拦截
其实我们在编写BTrace脚本时,是可以使用正则表达式匹配类名和方法名的,并非必须要指定一个完整的名称。如下示例:
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
@BTrace
public class PrintRegex {
@OnMethod(
// 类名也可以使用正则表达式进行匹配
clazz = "org.zero01.monitor_tuning.controller.BTraceController",
// 正则表达式需要写在两个斜杠内。
//拦截所有方法
method = "/.*/"
)
public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn) {
BTraceUtils.println("ClassName: " + pcn);
BTraceUtils.println("MethodName: " + pmn);
BTraceUtils.println();
}
}
6、获取方法的执行时间
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class TraceMethodTime {
//将字段声明为TheadLocal字段,每个线程拥有自己独立的字段
@TLS
private static long startTime = 0;
@OnMethod(
clazz = "com.example.designpattern.controller.BtraceController",
method ="/.*/",
location=@Location(Kind.ENTRY)
)
public static void methodStart(){
startTime = timeNanos();
}
@OnMethod(
clazz = "com.example.designpattern.controller.BtraceController",
method ="/.*/",
location=@Location(Kind.RETURN)
)
//@Duration 执行时间,单位纳秒,一般同 Kind.RETURN 和 Kind.ERROR 配合使用
public static void methodEnd(@Duration long uration){
//1秒=1000000000纳秒
long endTime = timeNanos();
println(strcat("execute time(nanos): ", str(endTime-startTime)));
println("spend time:"+(endTime-startTime));
}
}
7、定时获取Counter类的属性值totalCount。
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class TracingScript {
private static Object totalCount = 0;
@OnMethod(
clazz="com.learnworld.Counter",
method="add",
location=@Location(Kind.RETURN)
)
public static void traceExecute(@Self com.learnworld.Counter counter){
totalCount = get(field("com.learnworld.Counter","totalCount"), counter);
}
@OnTimer(1000)
public static void print(){
println("====== ");
println(strcat("totalCount: ",str(totalCount)));
}
}
8、定位某个超过阈值的函数
BTrace脚本
**
* 探测某个包路径下的方法执行时间是否超过某个阈值的程序,如果超过了该阀值,则打印当前线程的栈信息。
*/
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class PrintDurationTracer {
@OnMethod(clazz = "/com\\.techstar\\.monitordemo\\..*/", method = "/.*/", location = @Location(Kind.RETURN))
public static void trace(@ProbeClassName String pcn, @ProbeMethodName String pmn, @Duration long duration) {
//duration的单位是纳秒
if (duration > 1000 * 1000 * 2) {
BTraceUtils.println(Strings.strcat(Strings.strcat(pcn, "."), pmn));
BTraceUtils.print(" 耗时:");
BTraceUtils.print(duration);
BTraceUtils.println("纳秒,堆栈信息如下");
jstack();
}
}
}
拦截结果:
192:Btrace apple$ btrace 39644 PrintDurationTracer.java
com.techstar.monitordemo.controller.Adder.execute 耗时:1715294657纳秒,堆栈信息如下
com.techstar.monitordemo.controller.Adder.execute(Adder.java:13)
com.techstar.monitordemo.controller.Main.main(Main.java:10)
com.techstar.monitordemo.controller.Adder.execute 耗时:893795666纳秒,堆栈信息如下
com.techstar.monitordemo.controller.Adder.execute(Adder.java:13)
com.techstar.monitordemo.controller.Main.main(Main.java:10)
com.techstar.monitordemo.controller.Adder.execute 耗时:1331363658纳秒,堆栈信息如下
com.techstar.monitordemo.controller.Adder.execute(Adder.java:13)
9、性能分析
压测的时候经常发现某一个服务变慢了,但是由于这个服务有很多的业务逻辑和方法构成,这个时候就不好定位到底慢在哪个地方。BTrace可以解决这个问题,只需要大概定位问题可能存在的地方,通过包路径模糊匹配,就可以找到问题。
BTrace脚本
/**
*
* Description:
* This script demonstrates new capabilities built into BTrace 1.2
* Shortened syntax - when omitting "public" identifier in the class
* definition one can safely omit all other modifiers when declaring methods
* and variables
* Extended syntax for @ProbeMethodName annotation - you can use
* parameter to request a fully qualified method name instead of
* the short one
* Profiling support - you can use {@linkplain Profiler} instance to gather
* performance data with the smallest overhead possible
*/
@BTrace
class Profiling {
@Property
Profiler profiler = BTraceUtils.Profiling.newProfiler();
@OnMethod(clazz = "/com\\.techstar\\..*/", method = "/.*/")
void entry(@ProbeMethodName(fqn = true) String probeMethod) {
BTraceUtils.Profiling.recordEntry(profiler, probeMethod);
}
@OnMethod(clazz = "/com\\.techstar\\..*/", method = "/.*/", location = @Location(value = Kind.RETURN))
void exit(@ProbeMethodName(fqn = true) String probeMethod, @Duration long duration) {
BTraceUtils.Profiling.recordExit(profiler, probeMethod, duration);
}
@OnTimer(5000)
void timer() {
BTraceUtils.Profiling.printSnapshot("Performance profile", profiler);
}
}
10、死锁排查
我们怀疑程序是否有死锁,可以通过以下的脚步扫描追踪,非常简单方便。
/**
* This BTrace program demonstrates deadlocks
* built-in function. This example prints
* deadlocks (if any) once every 4 seconds.
*/
@BTrace
public class PrintDeadlock {
@OnTimer(4000)
public static void print() {
deadlocks();
}
}
11、分析哪个方法调用了System.gc(),调用栈如何?
12、方法执行时,查看对象的实例属性值
13、监控HashMap扩容时的上下文情况
https://blog.csdn.net/weixin_34244102/article/details/90682473
@BTrace
public class ExpandCapacityBtrace {
@OnMethod(clazz = "java.util.HashMap", method = "resize",
location = @Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/"))
public static void traceMapExpandCapacity(@ProbeClassName String probeClass, @ProbeMethodName String probeMethod,
@Self Object self,int newCapacity) {
String point = Strings.strcat(Strings.strcat(probeClass, "."), probeMethod);//java/util/HashMap.resize
Class clazz = classForName("java.util.HashMap");
println(Strings.strcat(point, "======"));
//获取实例protected变量
Map.Entry[] table= (Map.Entry[]) get(field(clazz, "table", true), self);
int threshold = getInt(field(clazz, "threshold", true), self);
int size = getInt(field(clazz, "size", true), self);
println(Strings.strcat("newCapacity:", str(newCapacity)));
println(Strings.strcat("table.length:", str(table.length)));
println(Strings.strcat("size:", str(size)));
println(Strings.strcat("threshold:", str(threshold)));
println(Strings.strcat(point, "------------"));
}
}
14、查看官方文档和更多的使用例子
打开解压后的安装包:
doc里就有文档。
samples就有很多的使用例子。
常用注解
1、@BTrace
声明这个类是个BTrace脚本.unsafe参数表示是否不安全的模式执行.
2、方法注解
- @OnMethod: 声明 探查点(probe point)
- clazz: 全路径类名,支持正则表达式,格式为/正则表达式/+类名 匹配子类@前缀 匹配anotation声明的类
- method: 方法名,支持正则表达式,格式为/正则表达式/, anotation使用@
- location: 用@Location来表明在什么时候时候去执行脚本
- Kind.ENTRY 进入方法时
- Kind.RETURN 方法返回时
- Kind.THROW 抛出异常时
- Kind.ARRAY_SET 设置数组元素时
- Kind.ARRAY_GET 获取数组元素时
- Kind.NEWARRAY 创建新数组时
- Kind.NEW 创建新对象时
- Kind.CALL 调用方法时
- Kind.CATCH 捕获异常时
- Kind.FIELD_SET 获取对象属性时
- Kind.FIELD_SET 设置对象属性时
- Kind.ERROR 方法由于发生未被捕获的异常结束时
- Kind.SYNC_ENTRY 进入同步块时
- Kind.SYNC_EXIT 离开同步块时
- type 方法类型, 不含方法名、参数民、异常声明。
- @OnTimer 定时器,间隔出发动作。参数: 间隔时间,单位毫秒
- @OnError BTrace代码发生异常时回调
- @OnExit BTrace代码调用exit来结束探测时回调该注释的方法
- @OnEvent 接受客户端事件时会回调。目前,当客户端命令上执行Ctrl-C SIGINT)时会发送一个时间到服务器端,从而触发@OnEvent注释的方法。
- @OnLowMemory 内存低于设置的阈值时回调方法
- pool 内存池名称
- threshold 阈值大小
- @OnProbe 支持使用xml格式来声明探测点点和探测动作。
3、字段注解
- @Export 将字段保罗给jstat访问
- @Property 将字段暴露注册为MBean 属性,可以通过JMX进行查看
- @TLS 将字段声明为TheadLocal字段,每个线程拥有自己独立的字段
4、方法形参注解
- @Self 声明探测的当前对象this
- @Return 方法返回对象
- @ProbeClassName 当前探测点所在的类名
- @ProbeMethodName 当前探测点所在的方法名
- fqn 是否获取全路径方法名称fully qualified name (FQN)
- @Duration 执行时间,单位纳秒,一般同 Kind.RETURN 和 Kind.ERROR 配合使用
- @TargetInstance 配合 Kind.CALL使用,声明了被调用方法所在的对象
- @TargetMethodOrField Kind.CALL使用,声明了被调用方法所在的对象的方法
注意事项
我们介绍了BTrace的常见用法,也编写了相应的demo进行一一演示。但是还有一些需要注意事项,在此说明一下:
- BTrace脚本默认只能本地运行,也就是只能调试本地的Java进程。如果需要在本地调试远程的Java进程的话,是需要自己去修改BTrace源码的
- BTrace脚本在生产环境下可以使用,但是被修改的字节码不会被还原。所以我们需要先在本地调试好BTrace脚本,然后才能放到生产环境下使用。并且需要注意BTrace脚本中不能含有影响性能或消耗资源较多的代码,不然会导致线上的服务性能降低。
https://blog.csdn.net/zuozewei/article/details/82635139
btrace 分析map:https://my.oschina.net/u/2474629/blog/1797075