1. 程式人生 > >基於Btrace的監控調試

基於Btrace的監控調試

方便 sam monitor super() 項目 -a [] private per

btrace快速入門

簡介:

BTrace是Java的安全可靠的動態跟蹤工具。 它的工作原理是通過 instrument + asm 來對正在運行的java程序中的class類進行動態增強。也就是說btrace可以在Java程序運行時,動態地向目標應用程序的字節碼註入追蹤代碼。

說他是安全可靠的,是因為它對正在運行的程序是只讀的。也就是說,他可以插入跟蹤語句來檢測和分析運行中的程序,不允許對其進行修改。因此他存在一些限制:

  • 不能創建對象
  • 不能創建數組
  • 不能拋出和捕獲異常
  • 不能調用任何對象方法和靜態方法
  • 不能給目標程序中的類靜態屬性和對象的屬性進行賦值
  • 不能有外部、內部和嵌套類
  • 不能有同步塊和同步方法
  • 不能有循環(for, while, do..while)
  • 不能繼承任何的類
  • 不能實現接口
  • 不能包含assert斷言語句

這些限制其實是可以使用unsafe模式繞過。通過在BTrace腳本中聲明 @BTrace(unsafe = true) 註解 ,並且使用 -u 選項,指定以 unsafe 模式運行btrace即可

註:實際使用非安全模式跟蹤時,發現一個問題,一個進程如果被安全模式btrace探測過一次, 後面再使用非安全模式進行探測時非安全模式不生效。

btrace的github地址:

https://github.com/btraceio/btrace

安裝btrace:

到github上下載btrace的壓縮包,我這裏下載的是1.3.11版本,下載地址如下:

https://github.com/btraceio/btrace/releases/tag/v1.3.11

解壓下載的壓縮包,解壓後可以看到目錄如下:
技術分享圖片

然後就是配置環境變量,首先創建一個BTRACE_HOME,該環境變量的值就是btrace的安裝目錄:
技術分享圖片

接著在path變量裏,配置BTRACE_HOME環境變量下的bin目錄:
技術分享圖片

配置好btrace後,我們有兩種方式可以運行BTrace腳本:

  • 一是在JVisualVM中添加BTrace插件,添加classpath
  • 二是直接使用命令行運行:btrace < pid > < trace_script >

我們先來演示第二種使用命令行的運行方式,在工程裏新建一個簡單的controller,用於演示如何利用BTrace腳本來實時獲取方法的參數值:

package org.zero01.monitor_tuning.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @program: monitor_tuning
 * @description: BTrace演示
 * @author: 01
 * @create: 2018-07-14 22:33
 **/

@RestController
@RequestMapping("/btrace")
public class BTraceController {

    @RequestMapping("/arg1")
    public String arg1(@RequestParam("name") String name) {
        return "hello: " + name;
    }
}

編寫完這個Controller後就可以啟動項目了,為了方便演示,我們直接將BTrace腳本寫在這個工程裏,實際上BTrace腳本與我們的項目代碼是分離的,沒有依賴關系的。但我們得引入編寫BTrace腳本所需的jar包,pom.xml配置的依賴如下

<dependency>
    <groupId>com.sun.btrace</groupId>
    <artifactId>btrace-agent</artifactId>
    <version>1.3.11</version>
    <type>jar</type>
    <scope>system</scope>
    <systemPath>E:\BTrace\btrace-bin-1.3.11\build\btrace-agent.jar</systemPath>
</dependency>
<dependency>
    <groupId>com.sun.btrace</groupId>
    <artifactId>btrace-boot</artifactId>
    <version>1.3.11</version>
    <type>jar</type>
    <scope>system</scope>
    <systemPath>E:\BTrace\btrace-bin-1.3.11\build\btrace-boot.jar</systemPath>
</dependency>
<dependency>
    <groupId>com.sun.btrace</groupId>
    <artifactId>btrace-client</artifactId>
    <version>1.3.11</version>
    <type>jar</type>
    <scope>system</scope>
    <systemPath>E:\BTrace\btrace-bin-1.3.11\build\btrace-client.jar</systemPath>
</dependency>

註:systemPath配置的是BTrace安裝目錄裏的jar包路徑,如果不想指向路徑的話,可以使用maven命令,把這些jar包安裝到本地的maven倉庫中,命令示例如下:

mvn install:install-file -Dfile=E:\BTrace\btrace-bin-1.3.11\build\btrace-boot.jar -DgroupId=com.sun.btrace -DartifactId=btrace-boot -Dversion=1.3.11 -Dpackaging=jar

然後新建一個btrace包,在該包內新建一個 PrintArgSimple 類,代碼如下:

package org.zero01.monitor_tuning.btrace;

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

/**
 * @program: monitor_tuning
 * @description: BTrace腳本
 * @author: 01
 * @create: 2018-07-14 22:33
 **/

@BTrace  // 註明這是一個BTrace腳本
public class PrintArgSimple {

    // 指定需要攔截的方法
    @OnMethod(
            // 類的路徑
            clazz = "org.zero01.monitor_tuning.controller.BTraceController",
            // 方法名
            method = "arg1",
            // 在什麽時候進行攔截
            location = @Location(Kind.ENTRY)
    )
    public static void anyRead(@ProbeClassName String pcn, // 被攔截的類名
                               @ProbeMethodName String pmn, // 被攔截的方法名
                               AnyType[] args // 被攔截的方法的參數值
    ) {
        // 打印數組
        BTraceUtils.printArray(args);
        // 打印行
        BTraceUtils.println("className: " + pcn);
        BTraceUtils.println("MethodName: " + pmn);
        BTraceUtils.println();
    }
}

打開命令行,進入到PrintArgSimple.java文件所在的路徑,然後使用命令運行該BTrace腳本:

C:\Users\admin>e:

E:\>cd E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace
E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>jps
11300
7396 Jps
8504 MonitorTuningApplication
5656 RemoteMavenServer
12188 Main
13916 Launcher

E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>btrace 8504 PrintArgSimple.java

註:因為代碼中有中文註釋,可能會報:編碼GBK的不可映射字符 錯誤,但是並不影響運行,可以不用管它

在命令行中運行了BTrace腳本後,到瀏覽器上,訪問我們之前所編寫的接口,如下:
技術分享圖片

訪問成功後,命令行輸出如下,可以看到BTrace腳本成功攔截了我們輸入的參數值,以及訪問的controller和方法:

[小明, ]
className: org.zero01.monitor_tuning.controller.BTraceController
MethodName: arg1

以上就是如何在命令行裏運行BTrace腳本。接著我們再來演示如何在JVisualVM裏運行BTrace腳本,在此之前我們還需要安裝一個BTrace的插件。關於BTrace插件的安裝,我已經在 基於JVisualVM的可視化監控 一文中介紹過了,這裏就不贅述了。復制好之前編寫的BTrace腳本,然後按照下圖粘貼到JVisualVM裏運行即可:
技術分享圖片

註:在JVisualVM裏運行BTrace腳本其實會有很多迷之問題,所以一般推薦使用命令行的運行方式


攔截構造函數、同名函數

在上一小節中,我們簡單介紹了BTrace的安裝以及兩種運行方式。並且也寫了一個小demo用於動態地攔截方法參數,而本節將介紹一下如何攔截構造函數和同名方法。

首先我們新建一個實體類,代碼如下:

package org.zero01.monitor_tuning.vo;

public class User {

    private int id;
    private String name;

    public User(int id, String name) {
        super();  // 必須加上super不然無法攔截到參數
        this.id = id;
        this.name = name;
    }

    ... gtter setter 略 ...
}

在BTraceController類中,新增如下方法,並啟動項目:

@RequestMapping("/constructor")
public User constructor(User user) {
    return user;
}

然後和之前一樣,在btrace包下新建一個BTrace腳本,代碼如下:

package org.zero01.monitor_tuning.btrace;

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

/**
 * @program: monitor_tuning
 * @description: 演示攔截構造函數
 * @author: 01
 * @create: 2018-07-14 22:33
 **/

@BTrace
public class PrintConstructor {

    @OnMethod(
            clazz = "org.zero01.monitor_tuning.vo.User",
            method = "<init>"  // 指定攔截構造函數
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
        BTraceUtils.println("ClassName: " + pcn);
        BTraceUtils.println("MethodName: " + pmn);
        BTraceUtils.printArray(args);
        BTraceUtils.println();
    }
}

同樣的,在命令行中運行該腳本:

E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>jps
1184 MonitorTuningApplication
11300
15208 Launcher
5656 RemoteMavenServer
1052 Jps

E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>btrace 1184 PrintConstructor.java

使用postman或瀏覽器訪問接口:
技術分享圖片

訪問成功後,命令行輸出如下,則是成功攔截到了:

ClassName: org.zero01.monitor_tuning.vo.User
MethodName: <init>
[1, 小明, ]

演示完構造函數的攔截後,然後我們來看看如何攔截同名的方法,在 BTraceController 類裏,增加如下兩個同名方法:

@RequestMapping("/same1")
public String same(@RequestParam("name") String name) {
    return "hello: " + name;
}

@RequestMapping("/same2")
public User same(@RequestParam("id") int id,
                 @RequestParam("name") String name) {
    return new User(id, name);
}

然後在btrace包下新建一個BTrace腳本,代碼如下:

package org.zero01.monitor_tuning.btrace;

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

@BTrace
public class PrintSame {

    @OnMethod(
            clazz = "org.zero01.monitor_tuning.controller.BTraceController",
            method = "same"
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, String name) {
        BTraceUtils.println("ClassName: " + pcn);
        BTraceUtils.println("MethodName: " + pmn);
        BTraceUtils.println("name: " + name);
        BTraceUtils.println();
    }

    @OnMethod(
            clazz = "org.zero01.monitor_tuning.controller.BTraceController",
            method = "same"
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, int id, String name) {
        BTraceUtils.println("ClassName: " + pcn);
        BTraceUtils.println("MethodName: " + pmn);
        BTraceUtils.println("id: " + id);
        BTraceUtils.println("name: " + name);
        BTraceUtils.println();
    }
}

從以上的BTrace腳本中可以看到,其實攔截重載方法的話,只需要在BTrace腳本的方法中聲明與之對應的參數即可。

接著就是在命令行裏運行該腳本,訪問相應的接口後,輸出如下:

E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>jps
9024 MonitorTuningApplication
11300
3956 Launcher
5656 RemoteMavenServer
9272 Jps

E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>btrace 9024 PrintSame.java
ClassName: org.zero01.monitor_tuning.controller.BTraceController
MethodName: same
name: 小明

ClassName: org.zero01.monitor_tuning.controller.BTraceController
MethodName: same
id: 1
name: 小明

攔截返回值、異常、行號

本小節將介紹常用的攔截方式,如何去攔截返回值、異常以及行號。

我們通過Kind來指定攔截方式,常用的攔截方式如下:

Kind.ENTRY  // 入口攔截,默認值
Kind.RETURN  // 攔截返回值
Kind.THROW  // 發生異常時攔截
Kind.LINE  // 攔截某一行

我們先來演示攔截返回值,就以之前編寫的arg1方法作為示例。

在btrace包下新建一個BTrace腳本,代碼如下:

package org.zero01.monitor_tuning.btrace;
import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
import com.sun.btrace.annotations.Return;

@BTrace
public class PrintReturn {

    @OnMethod(
            clazz="org.zero01.monitor_tuning.controller.BTraceController",
            method="arg1",
            location=@Location(Kind.RETURN)  // 攔截返回值
    )
    public static void anyRead(@ProbeClassName String pcn, 
                               @ProbeMethodName String pmn, 
                               @Return AnyType result // 接收返回值) {
        BTraceUtils.println("ClassName: " + pcn);
        BTraceUtils.println("MethodName: " + pmn);
        BTraceUtils.println("ResultValue: " + result);
        BTraceUtils.println();
    }
}

在命令行裏運行該腳本,訪問相應的接口後,輸出如下,可以看到成功攔截到了該方法的返回值:

E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>btrace 9024 PrintReturn.java
ClassName: org.zero01.monitor_tuning.controller.BTraceController
MethodName: arg1
ResultValue: hello: 小明

已經工作了的小夥伴們,應該會遇到一個常見的現象。那就是在很多舊項目的遺留代碼中,總是能看到很多不妥的處理異常的方式。例如經常能看到把異常使用try-catch包起來,但是又不打印異常堆棧,也不拋出去。結果就是把異常隱藏了起來,沒能讓異常終止邏輯,導致了代碼繼續執行,還無法定位問題的所在。就如同以下這個方法一樣:

@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這樣的調試工具,那麽就可以利用BTrace腳本來攔截異常並打印異常堆棧,從而定位問題。
在btrace包下新建一個BTrace腳本,代碼如下,這是我直接從官方示例裏拿來的代碼:

/*
 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the Classpath exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package org.zero01.monitor_tuning.btrace;

import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.Self;
import com.sun.btrace.annotations.TLS;

@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) {  // @Self其實就是攔截了this
        //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;
        }
    }
}

在命令行裏運行該腳本,訪問相應的接口後,輸出的異常堆棧如下:
技術分享圖片

可以看到,即便異常被隱藏了起來,而我們通過btrace腳本一樣能把異常重新給揪出來,這樣即便是線上正在運行的項目,我們也能夠不關閉、不重啟服務,就能夠監控到項目裏是否有發生異常。


我們還可以使用BTrace攔截某一行代碼,以此判斷該行代碼是否有被執行。在btrace包下新建一個BTrace腳本,代碼如下:

package org.zero01.monitor_tuning.btrace;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;

@BTrace
public class PrintLine {

    @OnMethod(
            clazz="org.zero01.monitor_tuning.controller.BTraceController",
            method="exception",
            location=@Location(value=Kind.LINE, line=43)  // 攔截第43行
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {
        BTraceUtils.println("ClassName: " + pcn);
        BTraceUtils.println("MethodName: " + pmn);
        BTraceUtils.println("line: " + line);
        BTraceUtils.println();
    }
}

在命令行裏運行該腳本,訪問相應的接口後,輸出的信息如下:

E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>btrace 14952 PrintLine.java
ClassName: org.zero01.monitor_tuning.controller.BTraceController
MethodName: exception
line: 43

如果沒有任何輸出的話,就代表那一行沒有被執行到,所以沒被攔截。這種攔截某一行的方式,不適用於判斷是否有異常,只能單純用於判斷某一行是否被執行了。


攔截復雜參數、環境變量、正則匹配攔截

在以上小節中,我們已經知道了如何攔截簡單的參入,本小節中,將介紹如何攔截復雜參數、環境變量以及使用正則匹配攔截。

攔截復雜參數就是攔截實體對象類型的參數,在 BTraceController 類裏,增加如下方法:

@RequestMapping("/arg2")
public User arg2(User user) {
    return user;
}

使用BTrace攔截復雜參數,需要使用反射的方式進行攔截,也就是需要傳遞包名+屬性名。在btrace包下新建一個BTrace腳本,代碼如下:

package org.zero01.monitor_tuning.btrace;

import java.lang.reflect.Field;

import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
import org.zero01.monitor_tuning.vo.User;

@BTrace
public class PrintArgComplex {

    @OnMethod(
            clazz = "org.zero01.monitor_tuning.controller.BTraceController",
            method = "arg2",
            location = @Location(Kind.ENTRY)
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, User user) {
        //print all fields
        BTraceUtils.print("print all fields: ");
        BTraceUtils.printFields(user);

        //print one field
        Field oneFiled = BTraceUtils.field("org.zero01.monitor_tuning.vo.User", "name");
        BTraceUtils.println("print one field: " + BTraceUtils.get(oneFiled, user));

        BTraceUtils.println("ClassName: " + pcn);
        BTraceUtils.println("MethodName: " + pmn);
        BTraceUtils.println();
    }
}

在命令行裏運行該腳本,訪問相應的接口後,輸出的信息如下:

E:\Java_IDEA\monitor_tuning\src\main\java\org\zero01\monitor_tuning\btrace>btrace -cp "E:\Java_IDEA\monitor_tuning\target\classes" 13336 PrintArgComplex.java
print all fields: {id=0, name=小明, }
print one field: 小明
ClassName: org.zero01.monitor_tuning.controller.BTraceController
MethodName: arg2

註:這裏使用到了一個 -cp 參數,該參數表示指定一個classpath路徑


其實我們在編寫BTrace腳本時,是可以使用正則表達式匹配類名和方法名的,並非必須要指定一個完整的名稱。如下示例:

package org.zero01.monitor_tuning.btrace;

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

@BTrace
public class PrintRegex {

    @OnMethod(
            // 類名也可以使用正則表達式進行匹配
            clazz = "org.zero01.monitor_tuning.controller.BTraceController",  
            // 正則表達式需要寫在兩個斜杠內
            method = "/.*/"  
    )
    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn) {
        BTraceUtils.println("ClassName: " + pcn);
        BTraceUtils.println("MethodName: " + pmn);
        BTraceUtils.println();
    }
}

通過編寫BTrace腳本我們可以打印出JVM的信息以及環境變量等,就類似於jinfo命令一樣。代碼示例:

package org.zero01.monitor_tuning.btrace;

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

@BTrace
public class PrintJinfo {
    static {
        // 打印系統屬性
        BTraceUtils.println("System Properties:");
        BTraceUtils.printProperties();

        // 打印JVM參數
        BTraceUtils.println("VM Flags:");
        BTraceUtils.printVmArguments();

        // 打印環境變量
        BTraceUtils.println("OS Enviroment:");
        BTraceUtils.printEnv();

        // 退出腳本
        BTraceUtils.exit(0);
    }
}

在命令行裏運行該腳本,輸出的信息如下:
技術分享圖片


註意事項

在以上小節中,我們介紹了BTrace的常見用法,也編寫了相應的demo進行一一演示。但是還有一些需要註意事項,在此說明一下:

  • BTrace腳本默認只能本地運行,也就是只能調試本地的Java進程。如果需要在本地調試遠程的Java進程的話,是需要自己去修改BTrace源碼的
  • BTrace腳本在生產環境下可以使用,但是被修改的字節碼不會被還原。所以我們需要先在本地調試好BTrace腳本,然後才能放到生產環境下使用。並且需要註意BTrace腳本中不能含有影響性能或消耗資源較多的代碼,不然會導致線上的服務性能降低。

基於Btrace的監控調試