转载

【深入浅出-JVM】(52):btrace

动态跟踪 java 运行程序,通过 hotswap 技术,动态将跟踪的字节码输入到运行类,对运行代码侵入小。

原理

instrument + asm

命令

  • btracec HelloWorld.java
    预编译,方便检查错误
  • btrace pid HelloWorld.java
    执行

例子

1.对一个周期性读取网络操作的程序,增加监控程序耗时程序

被监控的程序

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-07-22 15:49
 */
public class HoldNetTask implements Runnable {


    public static void main(String[] args) {
        new Thread(new HoldNetTask()).start();
    }


    public void visitWeb(String strUrl) {
        URL url = null;
        URLConnection urlConnection = null;
        InputStream is = null;

        try {
            url = new URL(strUrl);
            urlConnection = url.openConnection();
            is = urlConnection.getInputStream();

            BufferedReader buffer = new BufferedReader(new InputStreamReader(is));

            StringBuffer bs = new StringBuffer();
            String l = null;
            while ((l = buffer.readLine()) != null) {
                bs.append(l).append("/r/n");
            }


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    @Override
    public void run() {
        while (true) {
            visitWeb("http://www.sina.com.cn");
        }
    }
}

监控程序

import com.sun.btrace.annotations.*;

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

/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-07-22 15:58
 */
@BTrace
public class PrintTimes {

    @TLS
    private static long startTime = 0;

    @OnMethod(clazz = "/.+/",method = "/visitWeb/")   // visitWeb方法开始的执行执行
    public static void startMethod(){
        startTime = timeMillis();
    }



    @OnMethod(clazz = "/.+/",method = "/visitWeb/",location = @Location(Kind.RETURN))  //visitWeb方法结束的时候执行
    public static void endMethod(){
        print(strcat(strcat(name(probeClass()),"."),probeMethod()));
        print("[");
        print(strcat("Time taken: " , str(timeMillis() - startTime)));
        println("]");
    }

}

编写监控程序的时候,需要下载 btrace 客户端,maven 中配置该路径(中央仓库没有对应版本)

<!-- https://mvnrepository.com/artifact/io.btrace/btrace-maven -->
       <dependency>
           <groupId>com.sun.tools.btrace</groupId>
           <artifactId>btrace-agent</artifactId>
           <version>1.3.11.3</version>
           <scope>system</scope>
           <systemPath>/Users/mousycoder/My/software/btrace-bin-1.3.11.3/build/btrace-agent.jar</systemPath>
       </dependency>
       <dependency>
           <groupId>com.sun.tools.btrace</groupId>
           <artifactId>btrace-boot</artifactId>
           <scope>system</scope>
           <systemPath>/Users/mousycoder/My/software/btrace-bin-1.3.11.3/build/btrace-boot.jar</systemPath>
       </dependency>

       <dependency>
           <groupId>com.sun.tools.btrace</groupId>
           <artifactId>btrace-client</artifactId>
           <scope>system</scope>
           <systemPath>/Users/mousycoder/My/software/btrace-bin-1.3.11.3/build/btrace-client.jar</systemPath>
       </dependency>

先启动被监控程序 找到 pid 然后

btrace -v 16352 PrintTimes.java 即可开启监控程序

输出

DEBUG: received com.sun.btrace.comm.MessageCommand@2b515b5c
HoldNetTask.visitWebDEBUG: received com.sun.btrace.comm.MessageCommand@31ba0af0
[DEBUG: received com.sun.btrace.comm.MessageCommand@752ccee0
Time taken: 20DEBUG: received com.sun.btrace.comm.MessageCommand@28ec9c23

打印出了visitWeb执行方法的耗时

2.输出被监控程序的参数

import com.sun.btrace.AnyType;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;

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


/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-07-22 17:05
 */
@BTrace
public class PrintTimes2 {


    @OnMethod(clazz = "/.*HoldNetTask/",method = "/visitWeb/")
    public static void anyWriteFile(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args){
        print(pcn);
        print(".");
        print(pmn);
        printArray(args);
    }

}

输出

DEBUG: received com.sun.btrace.comm.MessageCommand@6f96c77
HoldNetTaskDEBUG: received com.sun.btrace.comm.MessageCommand@be64738
.DEBUG: received com.sun.btrace.comm.MessageCommand@3ba9ad43
visitWebDEBUG: received com.sun.btrace.comm.MessageCommand@49d904ec
[http://www.sina.com.cn, ]

可见已经输出了参数 http://www.sina.com.cn

3.输出被监控程序 27 行信息

import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.*;

import static com.sun.btrace.BTraceUtils.print;
import static com.sun.btrace.BTraceUtils.printArray;


/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-07-22 17:05
 */
@BTrace
public class AllLines {


    @OnMethod(clazz = "/.*HoldNetTask/",location = @Location(value = Kind.LINE,line = 27))
    public static void online(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line){
        print(BTraceUtils.Strings.strcat(pcn,"."));
        print(BTraceUtils.Strings.strcat(pmn,":"));
        print(line);
    }

}

输出

27DEBUG: received com.sun.btrace.comm.MessageCommand@39529185
HoldNetTask.DEBUG: received com.sun.btrace.comm.MessageCommand@72f926e6
visitWeb:DEBUG: received com.sun.btrace.comm.MessageCommand@3daa422a
27DEBUG: received com.sun.btrace.comm.MessageCommand@31c88ec8
HoldNetTask.DEBUG: received com.sun.btrace.comm.MessageCommand@1cbbffcd

4.新增定时任务(每秒输出虚拟机启动时间+每 3 秒导出线程堆栈)

import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnTimer;

import static com.sun.btrace.BTraceUtils.jstackAll;
import static com.sun.btrace.BTraceUtils.println;


/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-07-22 17:05
 */
@BTrace
public class Timers {

    @OnTimer(1000) //每秒运行
    public static void getUpTime(){
       println(BTraceUtils.Strings.strcat("1000 msec:", BTraceUtils.Strings.str(BTraceUtils.Sys.VM.vmUptime()))); //虚拟机启动时间
    }

    @OnTimer(3000) //每 3 秒运行
    public static void getStack(){
        jstackAll(); //导出线程堆栈
    }

}

输出

        com.sun.btrace.BTraceRuntime$19.run(BTraceRuntime.java:2387)
        java.util.TimerThread.mainLoop(Timer.java:555)
        java.util.TimerThread.run(Timer.java:505)

Thread[RMI Scheduler(0),9,system]

        sun.misc.Unsafe.park(Native Method)
        java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
        java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
        java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
        java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
        java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        java.lang.Thread.run(Thread.java:744)

DEBUG: received com.sun.btrace.comm.MessageCommand@3aeaafa6
1000 msec:1543007

5.打印实例属性

import com.sun.btrace.annotations.*;

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


/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-07-22 17:05
 */
@BTrace
public class PrintField {

    @OnMethod(clazz = "/.*URL/",method = "/.*openConnection/",location = @Location(value = Kind.ENTRY))
    public static void visitWebEntry(@Self Object self){
        println(strcat(strcat(name(probeClass()),"."),probeMethod()));
        println(self);
        println(get(field(classOf(self),"protocolPathProp"))); //只能取 static 变量
        println(get(field(classOf(self),"host"),self)); //获取实例变量
        println("===========");

    }

}

监控 URL 对象,在调用 openConnectio()方法的时候,获得当前实例 self ,然后获取属性

输出

DEBUG: received com.sun.btrace.comm.MessageCommand@3ba987b8
java.net.URL.openConnection
DEBUG: received com.sun.btrace.comm.MessageCommand@3f191845
http://www.sina.com.cn

6.谁调用了 GC

import com.sun.btrace.annotations.*;

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


/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019-07-22 17:05
 */
@BTrace
public class OnSystemGC {

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

}

7.更多例子可以查看 btrace 客户端目录下的 samples 文件夹

原文  http://mousycoder.com/thinking-in-jvm/52/
正文到此结束
Loading...