3. Btrace使用

1. Btrace簡介

1.1 概述

在周志明老師的深刻理解JVM虛擬機一書中,周老師這樣描述Btrace:在不中止目標程序運行的狀況下,經過HotSpot虛擬機的HotSwap技術動態加入本來並不存在的調試代碼。從這段話,咱們能夠獲得如下信息:1.使用Btrace能夠在不中止程序的狀況下進行調試。2.應用於HotSpot虛擬機,若是是其餘虛擬機,是不能用的。java

  • BTrace能夠動態地向目標應用程序的字節碼注入追蹤代碼;
  • 用到的技術:JavaComplier JVMTI Agent Instrumentation + ASM;
  • 默認只能本地運行,就是隻能調試本地的Java進程;
  • 生產環境下能夠使用,可是被修改的字節碼不會被還原;

1.1.1 安裝方法

項目地址git

  • 配置壞境變量BTRACE_HOME;
  • 配置Path,添加%BTRACE_HOME%bin;

1.1.2 兩種運行腳本方式

  • 在JVisualVM中添加Btrace插件,添加classpath
  • 使用命令行btrace <pid> <trace_script>
  • 注意BTrace腳本的項目要引入btrace-agent.jar,btrace-boot.jar,btrace-client.jar這3個依賴;

1.2 BTrace實戰

項目結構
btrace01.PNGgithub

pom.xmlweb

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>zte.hdh</groupId>
    <artifactId>monitor_tuning</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>monitor_tuning</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>asm</groupId>
            <artifactId>asm</artifactId>
            <version>3.3.1</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>

        <dependency>
            <groupId>com.sun.btrace</groupId>
            <artifactId>btrace-agent</artifactId>
            <version>1.3.11.3</version>
            <type>jar</type>
            <scope>system</scope>
            <systemPath>D:\ImportantDevTools\btrace-bin-1.3.11.3\build\btrace-agent.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.sun.btrace</groupId>
            <artifactId>btrace-boot</artifactId>
            <version>1.3.11.3</version>
            <type>jar</type>
            <scope>system</scope>
            <systemPath>D:\ImportantDevTools\btrace-bin-1.3.11.3\build\btrace-boot.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.sun.btrace</groupId>
            <artifactId>btrace-client</artifactId>
            <version>1.3.11.3</version>
            <type>jar</type>
            <scope>system</scope>
            <systemPath>D:\ImportantDevTools\btrace-bin-1.3.11.3\build\btrace-client.jar</systemPath>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.2.1 攔截參數

測試類(被攔截)
BTraceOneController.java:spring

package zte.hdh.btrace;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/btrace1")
public class BTraceOneController {
    @RequestMapping("/arg1")
    public String arg1(@RequestParam("name") String name) {
        return "hello," + name;
    }
}

Btrace類
BTraceArgSimple.java:apache

package zte.hdh.btrace;
import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.*;
@BTrace
public class BTraceArgSimple {
    @OnMethod(
            clazz="zte.hdh.btrace.BTraceOneController",//攔截的方類
            method="arg1",//攔截的方法
            location=@Location(Kind.ENTRY)//攔截的實際
    )
    
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {//AnyType[] args意思是全部參數,若是方法有兩個參數String int,那麼這裏就可寫String arg1,int arg2
        BTraceUtils.printArray(args);
        BTraceUtils.println(pcn);
        BTraceUtils.println(pmn);
        BTraceUtils.println();
    }
}

啓動應用後,獲取pid,到btrace腳本BTraceArgSimple.java的位置,運行btrace 48284 BTraceArgSimple.java,開始監控咱們的程序。在瀏覽器中輸入:http://localhost:8080/btrace1/arg1?name=hdh,那麼運行btrace腳本的控制檯將輸出攔截的參數、類和方法等信息:
btrace02.PNGjson

1.2.2 攔截方法

  • 普通方法 @OnMethod(clazz="",method="")
    clazz爲完整類名。
  • 構造方法 @OnMethod(clazz="",mewthod="<init>")
    clazz爲完成類名,用<init>的緣由是由於構造函數在字節碼層面就是這麼定義的,而btrace就是在字節碼層面進行攔截的。
  • 攔截同名函數,用參數進行區分

1.2.3 攔截時機

  • Kind.ENTRY:入口,默認值
  • Kind.RETURN:返回
  • Kind.THROW:異常
  • Kind.Line:行

1.2.4 捕獲方法返回值

PrintReturn.java瀏覽器

package zte.hdh.btrace;
import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.*;
@BTrace
public class PrintReturn {
    @OnMethod(
            clazz="zte.hdh.btrace.BTraceOneController",
            method="arg1",
            location=@Location(Kind.RETURN)
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, @Return AnyType result) {
        BTraceUtils.println(pcn + ", " + pmn + "," + result);
        BTraceUtils.println();
    }
}

1.2.5 打印異常信息

BTraceOneController.java中增長一個rest接口:app

@RequestMapping("/exception")
    public String exception() {
        try {
            System.out.println("start...");
            System.out.println(1/0);
            System.out.println("end...");
        }catch(Exception e) {
            //忘記打印出異常
        }
        return "success";
    }

btrace類PrintOnThrow.java:maven

package zte.hdh.btrace;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.*;
@BTrace
public class PrintOnThrow {
    // store current exception in a thread local
    // variable (@TLS annotation). Note that we can't
    // store it in a global variable!
    @TLS
    static Throwable currentException;
    // introduce probe into every constructor of java.lang.Throwable
    // class and store "this" in the thread local variable.
    @OnMethod(
            clazz="java.lang.Throwable",
            method="<init>"
    )
    public static void onthrow(@Self Throwable self) {//攔截new Throwable()
        currentException = self;
    }
    @OnMethod(
            clazz="java.lang.Throwable",
            method="<init>"
    )
    public static void onthrow1(@Self Throwable self, String s) {//攔截new Throwable(String msg)
        currentException = self;
    }
    @OnMethod(
            clazz="java.lang.Throwable",
            method="<init>"
    )
    public static void onthrow1(@Self Throwable self, String s, Throwable cause) {//攔截new Throwable(String msg, Throwable cause)
        currentException = self;
    }
    @OnMethod(
            clazz="java.lang.Throwable",
            method="<init>"
    )
    public static void onthrow2(@Self Throwable self, Throwable cause) {//攔截new Throwable(Throwable cause)
        currentException = self;
    }

    // when any constructor of java.lang.Throwable returns
    // print the currentException's stack trace.
    @OnMethod(
            clazz="java.lang.Throwable",
            method="<init>",
            location=@Location(Kind.RETURN)
    )
    public static void onthrowreturn() {
        if (currentException != null) {
            //打印整個異常堆棧
            BTraceUtils.Threads.jstack(currentException);
            BTraceUtils.println("=====================");
            currentException = null;
        }
    }
}

1.2.6 檢查某行是否執行

package zte.hdh.btrace;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.*;
@BTrace
public class PrintLine {
    @OnMethod(
            clazz="com.example.demo.btrace.Ch4Controller",
            method="exception",
            location=@Location(value= Kind.LINE, line=40)
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {
        BTraceUtils.println(pcn + ", " + pmn + "," + line);
        BTraceUtils.println();
    }
}
相關文章
相關標籤/搜索