博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
开发工具系列(一):Btrace——线上Debug工具
阅读量:2190 次
发布时间:2019-05-02

本文共 5068 字,大约阅读时间需要 16 分钟。

Btrace

Btrace用于调试正在运行的系统,并且在调试时不会暂停系统。特别适用于跟踪线上问题。你可以实时监控一个系统中任何一个方法的调用,你可以知道这些方法的参数、返回值是什么,还可以知道方法调用消耗了多少时间。

Btrace不需要安装,只要下载一个包,解压即可。

Btrace用法为bin/btrace <pid> <trace-script>。其中pid是正在运行的java进程,trace-script是跟踪脚本,它其实就是一段java代码。

Hello World

首先我们模拟一个正在运行的程序,它仅有一个循环。

package com.caipeichao;  
 
public class NullApp {
 
public static void main(String[] argv) {
new NullApp().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) {
}
}
}

然后开启这个程序: java com.caipeichao.NullApp

通过jps命令得到这个程序的PID,这里为13348。

> jps 
3034 RemoteMavenServer
2902 Main
15147 Jps
13348 NullApp

准备工作做完了,现在编写最重要的跟踪脚本。

import static com.sun.btrace.BTraceUtils.*;  
import com.sun.btrace.annotations.*;
 
@BTrace
public class HelloBtrace {
// 当com.caipeichao.NullApp.sleep方法返回时,执行该方法
@OnMethod(clazz="com.caipeichao.NullApp",
method="sleep",
location=@Location(Kind.RETURN))
public static void onSleep() {
println("Hello world");
}
}

运行btrace,得到如下输出。

> btrace 13348 HelloBtrace.java 
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world

常用注解

名称 作用域 作用
@BTrace 声明跟踪脚本
@OnMethod(clazz,method,location) 方法 当指定方法被调用时
@OnMethod(method="<init>") 方法 当构造函数被调用时
@OnMethod(clazz="/java\\.io\\..*Input/")) 方法 方法名称正则匹配
@Location(kind) @OnMethod 指定监控方法调用前还是调用后
@Location(value=Kind.NEWARRAY, clazz="char") @OnMethod 监控新增数组
@Self 参数 表示被监控的对象
@ProbeMethodName 参数 被监控的方法名称
@ProbeClassName 参数 被监控的类名
@OnTimer(interval) 方法 定时调用某个方法
@OnLowMemory(pool,threshold) 方法 当内存不足时
@OnExit 方法 当程序退出时
@OnProbe(namespace="java.net.socket",name="bind") 方法 监控socket中的bind方法

常用方法

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

原理

BTrace利用了java.lang.instrument包实现代码注入。首先通过VirtualMachine.attach(pid)连接远程JVM,然后通过VirtualMachine.loadAgent("*.jar")加载一个btrace的jar包。这个jar包最重要的代码如下。

public static void premain(String args, Instrumentation inst) {
  main(args, inst);
}
 
public static void agentmain(String args, Instrumentation inst) {
  main(args, inst);
}
 
// 将btrace的jar包添加到ClassLoader搜索目录 
private static synchronized void main(final String args, final Instrumentation inst) {
  ...
  inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(path)));
  ...
  inst.appendToSystemClassLoaderSearch(new JarFile(new File(path)));
  ...
  startServer();
}
 
// 开启服务 
private static void startServer() {
  ...
  while (true) {
    try {
      ...
      handleNewClient(client);
    } catch (RuntimeException re) {
      if (isDebug()) debugPrint(re);
    } catch (IOException ioexp) {
      if (isDebug()) debugPrint(ioexp);
    }
  }
}
 
// 修改内存中的类定义 
private static void handleNewClient(final Client client) {
  ...
  inst.addTransformer(client, true);
  ...
  inst.retransformClasses(classes);
}
 
// 用ASM动态生成字节码 
abstract class Client implements ClassFileTransformer, CommandListener {
static {
ClassFilter.class.getClass();
ClassReader.class.getClass();
ClassWriter.class.getClass();
...
}
 
private byte[] instrument(Class clazz, String cname, byte[] target) {
byte[] instrumentedCode;
try {
ClassWriter writer = InstrumentUtils.newClassWriter(target);
ClassReader reader = new ClassReader(target);
Instrumentor i = new Instrumentor(clazz, className, btraceCode, onMethods, writer);
...
}
}

一句话总结,btrace利用instrument工具修改JVM内存中的类字节码,达到注入代码的目的。

转载地址:http://wvyub.baihongyu.com/

你可能感兴趣的文章
梯度消失问题与如何选择激活函数
查看>>
为什么需要 Mini-batch 梯度下降,及 TensorFlow 应用举例
查看>>
为什么在优化算法中使用指数加权平均
查看>>
什么是 Q-learning
查看>>
用一个小游戏入门深度强化学习
查看>>
如何应用 BERT :Bidirectional Encoder Representations from Transformers
查看>>
5 分钟入门 Google 最强NLP模型:BERT
查看>>
走进JavaWeb技术世界11:单元测试框架Junit
查看>>
走进JavaWeb技术世界12:从手动编译打包到项目构建工具Maven
查看>>
走进JavaWeb技术世界13:Hibernate入门经典与注解式开发
查看>>
走进JavaWeb技术世界14:Mybatis入门
查看>>
走进JavaWeb技术世界16:极简配置的SpringBoot
查看>>
初探Java设计模式1:创建型模式(工厂,单例等)
查看>>
初探Java设计模式2:结构型模式(代理模式,适配器模式等)
查看>>
初探Java设计模式3:行为型模式(策略,观察者等)
查看>>
初探Java设计模式4:一文带你掌握JDK中的设计模式
查看>>
初探Java设计模式5:一文了解Spring涉及到的9种设计模式
查看>>
Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理
查看>>
Java集合详解2:一文读懂Queue和LinkedList
查看>>
Java集合详解3:一文读懂Iterator,fail-fast机制与比较器
查看>>