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();
    }
}
相关文章
相关标签/搜索