目录
  1. 安装
    1. 基本安装
    2. visualvm安装btrace
  2. 一个简单的例子
  3. 另一种获取函数运行时间的方法
  4. btrace直接使用项目中的类
  5. btrace关于map的操作
  6. 其他常见用法
    1. 打印调用栈
    2. 查看VM/内存/CPU等信息
  7. 参考
btrace简单使用

安装

基本安装

在github上btrace项目的release下 下载最新的btrace
https://github.com/btraceio/btrace/releases

解压完后,将btrace的bin目录添加进环境变量

使用方法 btrace <options> <PID> <btrace脚本>

visualvm安装btrace

首先在visualvm中安装btrace插件
菜单栏>工具>插件>可用插件>btrace

安装完成后,如下操作

一个简单的例子

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
35
36
37
38
39
40
41
42
43
package me.cayun.javalab;

public class BtraceMain {

private static Tmp output(String s, int x) {
for (int i = 0; i < 10000; i++) {
System.out.println(s + " " + x);
}

Tmp t = new Tmp();
t.setS(s);
t.setX(x);
return t;
}

public static void main(String[] args) throws InterruptedException {
while (true) {
output("hello", 30);
Thread.sleep(2000);
}
}
}

class Tmp {
private String s;
private int x;

public String getS() {
return s;
}

public void setS(String s) {
this.s = s;
}

public int getX() {
return x;
}

public void setX(int x) {
this.x = x;
}
}

目标

  1. 输出该函数的参数
  2. 输出该函数的执行时间
  3. 输出该函数的返回值

btrace脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

import com.sun.btrace.AnyType;
import java.lang.reflect.Field;

@BTrace
public class TracingScript {
/* put your code here */

@OnMethod(clazz="me.cayun.javalab.BtraceMain", method="output", location=@Location(Kind.RETURN))
public static void output(String s, int b, @Return AnyType result, @Duration long runningTime) {
println(strcat("arg1: ", s));
println(strcat("arg2: ", str(b)));

Class<?> clazz = classOf(result);
println(strcat("return: ", str(get(field(clazz, "s"), result))));
println(strcat("time: ", str(runningTime / 1000000)));
}
}

解释

  • @BTrace 表明这是一个btrace脚本
  • @OnMethod 指定方法入口,其中clazz和method参数可以使用正则表达式
  • @Location 表示拦截的时机,
    • Kind.ENTRY表示在进入方法时执行,默认为ENTRY
    • Kind.RETURN表示方法执行后执行
  • @Return 表示返回参数,前提location为Kind.RETURN
  • @Duration 表示函数执行时间,单位: 纳秒,前提location为Kind.RETURN
  • 获取类的某个属性值, get(field(clazz, name), object);

另一种获取函数运行时间的方法

还是刚刚那段Java代码,新的btrace脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
/* put your code here */

@TLS private static long startTime;

@OnMethod(clazz="me.cayun.javalab.BtraceMain", method="output")
public static void onCall() {
startTime = timeMillis();
}

@OnMethod(clazz="me.cayun.javalab.BtraceMain", method="output", location=@Location(Kind.RETURN))
public static void onReturn() {
println(str(timeMillis() - startTime));
}
}

解释

  • @TLS 用来将一个脚本变量与一个ThreadLocal变量关联

btrace直接使用项目中的类

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

public static AtomicInteger i = new AtomicInteger();

private static void inc(int x) {
i.addAndGet(x);
}

public static void main(String[] args) throws InterruptedException {
while (true) {
inc(new Random().nextInt(10));
System.out.println(i.get());
Thread.sleep(2000);
}
}
}

在第一个示例中,调用项目中的类使用的是反射的方式,但其实btrace也可以直接使用项目的类

目标

  1. 获取BtraceMain中的i的值

btrace脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

import me.cayun.javalab.BtraceMain;

@BTrace
public class TracingScript {
/* put your code here */

@OnMethod(clazz="me.cayun.javalab.BtraceMain", method="inc")
public static void onCall(@Self BtraceMain bt) {
println(str(bt.i));
}
}

emm~,只需要在前面import相应的类即可

如果变量为私有变量,仍然需要通过反射的方式获取

btrace关于map的操作

以下代码摘自btrace源码中com.sun.btrace.BTraceUtils函数

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
35
36
37
38
39
40
41
42
43
public static void printMap(Map map) {
BTraceRuntime.printMap(map);
}

public static <K, V> Map<K, V> newHashMap() {
return Collections.newHashMap();
}

public static <K, V> Map<K, V> newWeakMap() {
return Collections.newWeakMap();
}

public static <K, V> V get(Map<K, V> map, K key) {
return Collections.get(map, key);
}

public static <K, V> boolean containsKey(Map<K, V> map, K key) {
return Collections.containsKey(map, key);
}

public static <K, V> boolean containsValue(Map<K, V> map, V value) {
return Collections.containsValue(map, value);
}

public static <K, V> V put(Map<K, V> map, K key, V value) {
return Collections.put(map, key, value);
}

public static <K, V> V remove(Map<K, V> map, K key) {
return Collections.remove(map, key);
}

public static <K, V> void clear(Map<K, V> map) {
Collections.clear(map);
}

public static <K, V> int size(Map<K, V> map) {
return Collections.size(map);
}

public static <K, V> boolean isEmpty(Map<K, V> map) {
return Collections.isEmpty(map);
}

其他常见用法

打印调用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package me.cayun.javalab;

public class BtraceMain {

private static void gc1() {
gc2();
}

private static void gc2() {
gc3();
}

private static void gc3() {
System.gc();
}

public static void main(String[] args) throws InterruptedException {
while (true) {
gc1();
Thread.sleep(10000);
}
}
}

btrace脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
/* put your code here */

@OnMethod(clazz="java.lang.System", method="gc")
public static void onCallGC() {
jstack();
}
}

查看VM/内存/CPU等信息

可以参考BTraceUtils的源码
https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/BTraceUtils.java

参考

[1] btrace github
[2] BTrace 简明使用手册
[3] BTrace使用

文章作者: 谷河
文章链接: https://www.lyytaw.com/java/btrace%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 谷河|BLOG