Java在线问题排除利器

BTrace

BTrace在不影响目标程序运行的前提下,通过HotSpot虚拟机HotSwap技术动态插入调试代码,相关原理参考博文:https://victorzhzh.iteye.com/blog/965789
官网:https://github.com/btraceio/btrace
1)安装

wget https://github.com/btraceio/btrace/releases/download/v1.3.11.3/btrace-bin-1.3.11.3.tgz
mkdir btrace
cd trace
tar zxvf btrace-bin-1.3.11.3.tgz -C btrace/

2)示例程序

import java.util.Scanner;

public class Calculator {
        public int add(int a, int b) throws Exception {
                Thread.sleep(1000);
                return a + b;
        }
        public static void main(String[] args) throws Exception {
                Scanner scanner = new Scanner(System.in);
                Calculator obj = new Calculator();
                while ( scanner.hasNext() ) {
                        int a = scanner.nextInt();
                        int b = scanner.nextInt();
                        int c = obj.add(a, b);
                        System.out.println(String.format("%d + %d = %d", a, b, c));
                }
        }
}

3)调试脚本

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
 
@BTrace
public class TracingScript {
    @OnMethod(
        clazz="Calculator",
        method="add",
        location=@Location(Kind.RETURN)
    )
     
    public static void func(
            int a, 
            int b,
            @Return int result,
            @ProbeClassName String probeClass,
            @ProbeMethodName String probeMethod,
            @Duration long duration) {
        // 如果执行时间大于 500ms,则打印花费的时间和堆栈
        if(duration / 1000000 > 500) {
	        println("trace: =======================");
	        jstack();
	        println(strcat("a:", str(a)));
	        println(strcat("b:", str(b)));
	        println(strcat("result:", str(result)));
	        println("== entered " + probeClass + "." + probeMethod);
	        println("== spend: " + duration /1000000 + " ms");
        }
    }
}

4)运行调试

javac Calculator.java  # 编译程序
java Calculator # 运行程序

cd trace
./bin/btracec TracingScript.java #编译调试脚本
jps -l # 查找Calculator程序进程ID
./bin/btrace -p port pid TracingScript.java #调试进程

5)注解说明

方法注解说明

@OnMethod:指定使用当前注解的方法应该在什么情况下触发:
claszz属性指定要匹配的类的全限定类名,可以用正则表达式;
method属性指定要匹配的方法名称,可以用正则表达式;
type属性void(java.lang.String)可以用于匹配:public void funcName(String param) 中的方法入参;
location属性用@Location来表明,匹配了clazz,method情况,在方法执行的何时去执行脚本(前,后,异常,行,某个方法调用)
@OnTimer:指定一个定时任务
@OnExit:当脚本运行Sys.exit(code)时触发
@OnError:当脚本运行抛出异常时触发
@OnEvent:脚本运行时Ctrl+C可以发送事件
@OnLowMemory:让你指定一个阀值,内存低于阀值触发
@OnProbe:可以用一个xml文件来描述你想在什么时候触发该方法

方法参数注解说明

@Self:目标对象本身
@Retrun:目标程序方法返回值(Kind.RETURN)
@ProbeClassName:目标类名
@ProbeMethodName:目标方法名
@targetInstance:@Location指定的clazz,method的目标(Kind.CALL)
@targetMethodOrField:@Location指定的clazz,method的目标的方法或字段(Kind.CALL)
@Duration:目标方法执行时间,单位是纳秒,需要与 Kind.RETURN 或者 Kind.ERROR 一起使用

Greys

Greys是命令交互式的,直接输入监控类和方法即可监控调试,每次只能监控一个方法。
详细介绍:https://yq.aliyun.com/articles/2390
1)安装

wget http://ompc.oss.aliyuncs.com/greys/release/greys-1.7.6.4-bin.zip
unzip greys-1.7.6.4-bin.zip -d .
cd greys
./install-local.sh

2)调试

./greys.sh pid
trace -n 2 Calculator add '#cost>500' # 耗时大于500ms时,打印堆栈信息
tt -t -n 2 Calculator add

3)退出

退出前可以使用 reset 恢复增强类(即被动态修改的代码)
最后,使用shutdown 关闭greys 并退出

总结说明

(1) 相比两个工具,btrace 需要手写脚步,每次更新都要重新上传再执行,而greys 支持命令式交互,无需手写脚本;
(2)btrace 脚步中,可以写多个监听类和方法,但是greys 命令同时只能输入一个。(但是greys 可以支持多个用户操作,所如果想同时监控多个方法,只能开多窗口)
(3)btrace 要确保监控脚本的正确性,使用前最好预编译,防止动态增强后影响在线功能;
(4)监控时,设置合适的条件,例如在 greys实例中,花费时间大于 N ms才输出,且只打印2个。
(5)greys 中只能显示1层的方法调用情况,无法直接跟踪到最底层;只能自己一层一层往下跟进。

猜你喜欢

转载自blog.csdn.net/m0_37261091/article/details/87904431