专注于快乐的事情

btrace学习

#功能介绍?

Btrace (Byte Trace)是sun推出的一款java 动态、安全追踪(监控)工具,可以不停机的情况下监控线上情况,并且做到最少的侵入,占用最少的系统资源。

BTrace利用了java.lang.instrument包实现代码注入
通过VirtualMachine.attach(pid)连接远程JVM,然后通过VirtualMachine.loadAgent(“*.jar”)加载一个btrace的jar包
通过ASM动态生成字节码。

基本使用

运行一个Java程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class MockApp {

public static void main(String[] argv) {
new MockApp().run();
}

public void run() {
for (int i = 0; i < 100000; i++) {
sleep(1000);
new MyObj().life(i);
}
}

private static class MyObj {

public void life(int n) {
System.out.println(n);
}
}

private void sleep(int n) {
try {
Thread.sleep(n);
} catch (InterruptedException e) {
}
}
}

jps命令查出需要监控的jvm pid

jps

4144 Jps
586
4141 AppMain
4142 Launcher

可以看到pid为4142

每次都找,很麻烦,可以使用:jps | grep AppMain | awk '{print $1}' | xargs -I {} btrace {} sample/HelloBtrace.java

编写BTrace跟踪程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package sample;

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 static com.sun.btrace.BTraceUtils.println;

@BTrace
public class HelloBtrace {
// app.MockApp.sleep方法返回时,执行该方法
@OnMethod(clazz="app.MockApp",
method="sleep",
location=@Location(Kind.RETURN))
public static void onSleep() {
println("Hello world");
}
}

执行btrace 跟踪程序

btrace 4141 sample/HelloBtrace.java

常用注解

类上的注解

名称 作用域 作用
@BTrace 用来指定该java类为一个btrace脚本文件
@DTrace 指定btrace脚本与内置在其脚本中的D语言脚本关联
@DTraceRef 指定btrace脚本与另一个D语言脚本文件关联

方法上的注解

名称 作用域 作用
@OnMethod(clazz,method,location) 方法 当指定方法被调用时
@OnMethod(method=”“) 方法 当构造函数被调用时
@OnMethod(clazz=”/java\.io\..*Input/“)) 方法 方法名称正则匹配
@Location(kind) @OnMethod 指定监控方法调用前还是调用后
@Location(value=Kind.NEWARRAY,clazz=”char”) @OnMethod
@OnTimer(interval) 方法 定时调用某个方法
@OnLowMemory(pool,threshold) 方法 当内存不足时
@OnExit 方法 当程序退出时
@OnProbe(namespace=”java.net.socket”,name=”bind”) 方法 监控socket中的bind方法

参数上的注解

名称 作用域 作用
@Self 参数 表示被监控的对象
@Return 参数 用来指定被trace方法的返回值
@ProbeMethodName 参数 被监控的方法名称
@ProbeClassName 参数 被监控的类名

非注解的方法参数

未使用注解的方法参数一般都是用来做方法签名匹配用的, 他们一般和被trace方法中参数出现的顺序一致. 不过他们也可以与注解方法交错使用, 如果一个参数类型声明为AnyType[], 则表明它按顺序”通吃”方法所有参数. 未注解方法需要与Location结合使用:

方法 作用
Kind.ENTRY 被trace方法参数
Kind.RETURN 被trace方法返回值
Kind.THROW 抛异常
Kind.ARRAY_SET, Kind.ARRAY_GET 数组索引
Kind.CATCH 捕获异常
Kind.FIELD_SET 属性值
Kind.LINE 行号
Kind.NEW 类名
Kind.ERROR 抛异常

Kind.ENTRY 是和 Kind.RETURN 对应的,

@OnMethod(location = @Location(Kind.RETURN)) 表示目标方法返回时触发;ENTRY表示进入时触发。

Kind.CALL 和 Kind.LINE 作用类似
@OnMethod(location = @Location(value = Kind.CALL, clazz = “a”, method = “b”)) 指目标方法体如果调用了a.b()方法时触发;

@OnMethod(location = @Location(value = Kind.LINE, line = 5)) 指目标方法体执行到第n行代码时触发

属性上的注解

常用方法

方法 作用
println 在本地控制台输出一行
print 在本地控制台输出
printArray 在本地控制台输出数组
jstack 打印远程方法的调用调用栈
jstackAll 输出所有线程的调用栈
exit 退出跟踪脚本
Strings.strcat 连接字符串
Reflactive.name 获取类名
Threads.name 线程名
Threads.currentThread 当前线程
deadlocks 打出死锁线程
sizeof 获取对象的大小,比如List对象就返回List.size()
Sys.Env.property 获取系统变量

参考例子

打印堆/非堆内存信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package sample; /**
* Created by ww on 16/11/15.
*/
import com.sun.btrace.annotations.BTrace;

import com.sun.btrace.annotations.OnTimer;

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

@BTrace

public class TraceMemory {

//heapUsage()/nonHeapUsage() – 打印堆/非堆内存信息,包括init、used、commit、max

//init = 134217728(131072K) used = 16793216(16399K) committed = 128974848(125952K) max = 1908932608(1864192K)

@OnTimer(4000)

public static void printM(){

//打印内存信息

println("heap:");

println(heapUsage());

println("no-heap:");

println(nonHeapUsage());

}

}

打印参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package sample;



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.Return;
import com.sun.btrace.annotations.Self;

import java.lang.reflect.Field;

import static com.sun.btrace.BTraceUtils.*;
import static com.sun.btrace.BTraceUtils.Reflective.field;
import static com.sun.btrace.BTraceUtils.println;

@BTrace
public class PrintParameterTest {
@OnMethod(clazz="app.BTraceServer", method="sayHello",
location=@Location(Kind.RETURN))
public static void onSayHello(@Self Object s, AnyType parames, @Return String ret){
Field maxTotalField = field("app.User", "name");

println(strcat("Ps Key: ", str(get(maxTotalField, parames))));
println("sdfsdf");
}
}

其中,parames 是一个User对象。切记只能为AnyType对象,我开始时使用的是Object,输出就为空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BTraceServer {

public String sayHello(User user) {
return " 结果:" + user.getName();
}

public static void main(String[] args) throws InterruptedException {
BTraceServer bt = new BTraceServer();
int i = 0;
while (true) {
System.err.println(bt.sayHello(new User(i++,"name" +i)));
Thread.sleep(1000);
}
}
}

参考

评论系统未开启,无法评论!