BTrace 問題輔助排查工具使用手冊

  BTrace是調試神器,能夠經過本身編寫的腳本,獲取應用的一切調用信息。而不須要重啓應用!java

Btrace 項目源碼信息(你行你上~)linux

項目地址:http://github.com/btraceio/btracegit

可是應用下載地址是: https://github.com/btraceio/btrace/releases   (由於通常你並不想編譯這些代碼)github

 

小白的打開姿式,操做步驟:正則表達式

  1. 打開 jvisualVm 工具;
  2. 加載 BTrace 工具, 先把 插件中心地址更改掉: https://visualvm.github.io/uc/8u131/updates.xml.gz ;
  3. 鏈接到想要 trace 的服務器, 如本地 tomcat;
  4. 右擊tomcat進程,> Trace application...
  5. 寫debug程序,樣例以下:(在IDE中編寫)spring

  6. 直接使用 brace監控: btrace <pid> BtraceScript.java 緩存

 

 

來個例子:(攔截 spring 的 doService 方法)tomcat

/* BTrace Script Template */

import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Duration;
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 static com.sun.btrace.BTraceUtils.currentThread;
import static com.sun.btrace.BTraceUtils.probeLine;
import static com.sun.btrace.BTraceUtils.threadId;

@BTrace
public class TracingScript {
    /* put your code here */
    @OnMethod(
            clazz = "/org.springframework.servlet.+/",
            method = "/doService.*/",
            location = @Location(Kind.RETURN)
    )
    public static void traceExecute(AnyType[] args, @ProbeClassName String name, @ProbeMethodName String method, @Duration long time) {
        long durationTime = time/1000000;
        if(durationTime > 0){
            String output = name + "." + method + "#" + probeLine() + " cost: " + durationTime + "ms, ThreadId:" + threadId(currentThread());
            BTraceUtils.println(output);
            // 打印總體參數
            BTraceUtils.printArray(args);
            // 調用應用的各字段進行反射調用
            BTraceUtils.printFields(args[1]);
            BTraceUtils.printFields(args[2]);
            BTraceUtils.println("over...");
            // 結束符
            BTraceUtils.println("");
        }
    }
}

 

在IDE中編寫時,須要導入 pom.xml 以下:springboot

        <!-- https://mvnrepository.com/artifact/com.sun.tools.btrace/btrace-agent -->
        <dependency>
            <groupId>com.sun.tools.btrace</groupId>
            <artifactId>btrace-agent</artifactId>
            <version>1.1.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.sun.tools.btrace/btrace-boot -->
        <dependency>
            <groupId>com.sun.tools.btrace</groupId>
            <artifactId>btrace-boot</artifactId>
            <version>1.1.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.sun.tools.btrace/btrace-client -->
        <dependency>
            <groupId>com.sun.tools.btrace</groupId>
            <artifactId>btrace-client</artifactId>
            <version>1.1.3</version>
        </dependency>

 


2. 攔截方法定義,也說是 @OnMethod 註解的做用
  Btrace使用@OnMethod註解定義須要分析的方法入口,在@OnMethod註解中,須要指定class、method以及location等,class代表須要監控的類,method代表須要監控的方法!服務器

@OnMethod(
            clazz = "/org.springframework.servlet.+/",
            method = "/doService.*/",
            location = @Location(Kind.RETURN)
    )

  1. 正則表達式定位(全匹配是正則的一種特例) 

    能夠用表達式,批量定義須要監控的類與方法。正則表達式須要寫在兩個 "/" 中間。
    經過在攔截函數的定義裏注入@ProbeClassName String probeClass, @ProbeMethodName String probeMethod 參數,告訴腳本實際匹配到的類和方法名。
  2. 按接口,父類,Annotation定位

  好比我想匹配全部的Filter類,在接口或基類的名稱前面,加個+ 就行

@OnMethod(clazz="+com.vip.demo.Filter", method="doFilter")

  也能夠按類或方法上的annotaiton匹配,也就是註解匹配,前面加上@就行

@OnMethod(clazz="@javax.jws.WebService", method="@javax.jws.WebMethod")

  3. 構造方法匹配  <init>

@OnMethod(clazz="java.net.ServerSocket", method="<init>")

  4. 靜態內部類的寫法,在類與內部類之間加上"$"

@OnMethod(clazz="com.vip.MyServer$MyInnerClass", method="hello")

3. 攔截時機, 即@Location註解的做用

// Location 的定義
/**
 * This annotation specifies a particular "location" within a
 * traced/probed java method for BTrace probe specifications.
 *
 * @author A. Sundararajan
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Location {
    /**
     * Kind of the location.
     *
     * @see Kind
     */
    Kind value() default Kind.ENTRY;

    /**
     * Specifies where do want to probe with
     * respect to the location of interest.
     *
     * @see Where
     */
    Where where() default Where.BEFORE;

    /**
     * Specifies the fully qualified class name for
     * certain kind of probe locations.
     *
     * <p>
     * <h3>Since 1.3.11</h3>
     * The specification can contain references to user arguments.
     * These references are using Ant style substitution patterns.
     * If a reference is not resolvable the whole probe point will be effectively disabled.
     * <br>
     * <pre>
     * {@code @OnMethod(clazz = "MyClass", method = "myMethod", location = @Location(clazz = "${package}.OtherClass"))}
     * </pre>
     * </p>
     */
    String clazz() default "";

    /**
     * Specifies the method name for
     * certain kind of probe locations.
     *
     * <p>
     * <h3>Since 1.3.11</h3>
     * The specification can contain references to user arguments.
     * These references are using Ant style substitution patterns.
     * If a reference is not resolvable the whole probe point will be effectively disabled.
     * <br>
     * <pre>
     * {@code @OnMethod(clazz = "MyClass", method = "myMethod", location = @Location(clazz = "OtherClass", method = "${method}"))}
     * </pre>
     * </p>
     */
    String method() default "";

    /**
     * Specifies the field name for Kind.FIELD_SET
     * and Kind.FIELD_GET probes.
     *
     * @see Kind#FIELD_GET
     * @see Kind#FIELD_SET
     *
     * <p>
     * <h3>Since 1.3.11</h3>
     * The specification can contain references to user arguments.
     * These references are using Ant style substitution patterns.
     * If a reference is not resolvable the whole probe point will be effectively disabled.
     * <br>
     * <pre>
     * {@code @OnMethod(clazz = "MyClass", method = "myMethod", location = @Location(clazz = "OtherClass", field = "${field}"))}
     * </pre>
     * </p>
     */
    String field() default "";

    /**
     * Specifies field or method type for
     * certain kind of probe locations. The type
     * is specified like in Java source - except
     * the method or field name and parameter names
     * are not included.
     *
     * <p>
     * <h3>Since 1.3.11</h3>
     * The specification can contain references to user arguments.
     * These references are using Ant style substitution patterns.
     * If a reference is not resolvable the whole probe point will be effectively disabled.
     * <br>
     * <pre>
     * {@code @OnMethod(clazz = "MyClass", method = "myMethod", location = @Location(clazz = "OtherClass", type = "${ret} ()"))}
     * </pre>
     * </p>
     */
    String type() default "";

    /**
     * Specifies the line number for Kind.LINE probes.
     *
     * @see Kind#LINE
     */
    int line() default 0;
}

// Kind 的定義
public enum Kind {
    /**
     * <h2>Array element load</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@code type[]} - the array instance</li>
     *   <li>{@link int int} - array index</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li>
     * </ul>
     */
    ARRAY_GET,

    /**
     * <h2>Array element store</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@code type[]} - the array instance</li>
     *   <li>{@link int int} - array index</li>
     *   <li>{@link java.lang.Object Object} - new value</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     * </ul>
     */
    ARRAY_SET,

    /**
     * <h2>Method call</h2>
     * <p>
     * The order and number of unannotated parameters (if provided) must
     * fully match the called method signature. Instead of specific parameter
     * types one can use {@linkplain AnyType} to match any type.
     * <p>
     * If the only unannotated parameter is of type {@link AnyType AnyType[]}
     * it will contain the called method parameters in the order defined by
     * its signature.
     *
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain TargetInstance} - the target instance of the method call
     *       or null if the method is static</li>
     *   <li>{@linkplain TargetMethodOrField} - the name of the method which is called</li>
     *   <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li>
     *   <li>{@linkplain Duration} - the method call duration in nanoseconds (only for {@linkplain Where#AFTER}</li>
     * </ul>
     */
    CALL,

    /**
     * <h2>Exception catch</h2>
     *
     * <p>
     * The order and number of unannotated parameters (if provided) must
     * fully match the probed method signature. Instead of specific parameter
     * types one can use {@linkplain AnyType} to match any type.
     * <p>
     * If the only unannotated parameter is of type {@link AnyType AnyType[]}
     * it will contain the probed method parameters in the order defined by
     * its signature.
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain TargetInstance} - the caught {@linkplain Throwable} (@since 1.3.11)</li>
     * </ul>
     */
    CATCH,

    /**
     * <h2>Checkcast</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@link java.lang.String String} - type to cast to</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain TargetInstance} - the casted instance ({@linkplain AnyType})</li>
     * </ul>
     */
    CHECKCAST,

    /**
     * <h2>Method entry</h2>
     * <p>
     * The order and number of unannotated parameters (if provided) must
     * fully match the probed method signature. Instead of specific parameter
     * types one can use {@linkplain AnyType} to match any type.
     * <p>
     * If the only unannotated parameter is of type {@link AnyType AnyType[]}
     * it will contain the probed method parameters in the order defined by
     * its signature.
     *
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     * </ul>
     */
    ENTRY,

    /**
     * <h2>"return" because of no-catch</h2>
     *
     * <p>
     * The order and number of unannotated parameters (if provided) must
     * fully match the probed method signature. Instead of specific parameter
     * types one can use {@linkplain AnyType} to match any type.
     * <p>
     * If the only unannotated parameter is of type {@link AnyType AnyType[]}
     * it will contain the probed method parameters in the order defined by
     * its signature.
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain Duration} - the method call duration in nanoseconds (only for {@linkplain Where#AFTER}</li>
     *   <li>{@linkplain TargetInstance} - the {@linkplain Throwable} instance (@since 1.3.11)</li>
     * </ul>
     */
    ERROR,

    /**
     * <h2>Getting a field value</h2>
     *
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain TargetInstance} - the field owner instance or null 
     *       if the field is static</li>
     *   <li>{@linkplain TargetMethodOrField} - the name of the method which is called</li>
     *   <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li>
     * </ul>
     */
    FIELD_GET,

    /**
     * <h2>Setting a field value</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@link java.lang.Object Object} - new field value</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that field is static</li>
     *   <li>{@linkplain TargetInstance} - the field owner instance or null 
     *       if the field is static</li>
     *   <li>{@linkplain TargetMethodOrField} - the name of the method which is called</li>
     * </ul>
     */
    FIELD_SET,

    /**
     * <h2>instanceof check</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@link java.lang.String String} - type to check against</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain TargetInstance} - the checked instance ({@linkplain AnyType})</li>
     * </ul>
     */
    INSTANCEOF,

    /**
     * <h2>Source line number</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@link int int} - line number</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     * </ul>
     */
    LINE,

    /**
     * <h2>New object created</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@link java.lang.String String} - object type name</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li>
     * </ul>
     */
    NEW,

    /**
     * <h2>New array created</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@link java.lang.String String} - array type name</li>
     *   <li>{@link int int} - number of dimensions</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li>
     * </ul>
     */
    NEWARRAY,

    /**
     * <h2>Return from method</h2>
     * <p>
     * The order and number of unannotated probe handler parameters (if provided)
     * must fully match the probed method signature. Instead of specific parameter
     * types one can use {@linkplain AnyType} to match any type.
     * <p>
     * If the only unannotated parameter is of type {@link AnyType AnyType[]}
     * it will contain the probed method parameters in the order defined by
     * its signature.
     *
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain Return} - the return value of the method call (only for {@linkplain Where#AFTER})</li>
     *   <li>{@linkplain Duration} - the method call duration in nanoseconds (only for {@linkplain Where#AFTER}</li>
     * </ul>
     */
    RETURN,

    /**
     * <h2>Entry into a synchronized block</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@link java.lang.Object Object} - lock object</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     * </ul>
     */
    SYNC_ENTRY,

    /**
     * <h2>Exit from a synchronized block</h2>
     *
     * <h3>Unannotated probe handler parameters:</h3>
     * <ol>
     *   <li>{@link java.lang.Object Object} - lock object</li>
     * </ol>
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     * </ul>
     */
    SYNC_EXIT,

    /**
     * <h2>Throwing an exception</h2>
     *
     * <p>
     * The order and number of unannotated parameters (if provided) must
     * fully match the probed method signature. Instead of specific parameter
     * types one can use {@linkplain AnyType} to match any type.
     * <p>
     * If the only unannotated parameter is of type {@link AnyType AnyType[]}
     * it will contain the probed method parameters in the order defined by
     * its signature.
     * <h3>Allowed probe handler parameter annotations:</h3>
     * <ul>
     *   <li>{@linkplain ProbeClassName} - the name of the enclosing class</li>
     *   <li>{@linkplain ProbeMethodName} - the name of the enclosing method</li>
     *   <li>{@linkplain Self} - the instance enclosing the declaring method or null
     *       if that method is static</li>
     *   <li>{@linkplain TargetInstance} - the thrown exception (@since 1.3.11)</li>
     * </ul>
     */
    THROW
};

  Location 主要屬性有 value 和 where, 而 value 則是幾個經常使用的定義點!

  定義Btrace對方法的攔截位置,經過@Location註解指定,默認爲Kind.ENTRY。能夠爲同一個函數的不一樣的Location,分別定義多個攔截函數。
  1. Kind.Entry與Kind.Return
    Kind.ENTRY:在進入方法時,調用Btrace腳本
    Kind.RETURN:方法執行完時,調用Btrace腳本,只有把攔截位置定義爲Kind.RETURN,才能獲取方法的返回結果@Return和執行時間@Duration
    duration的單位是納秒,要除以 1,000,000 纔是毫秒。
  2. Kind.Error, Kind.Throw和 Kind.Catch
    異常拋出(Throw),異常被捕獲(Catch),異常沒被捕獲被拋出函數以外(Error),主要用於對某些異常狀況的跟蹤。
    在攔截函數的參數定義裏注入一個Throwable的參數,表明異常。
  3. Kind.Call與Kind.Line
    Kind.CALL:分析方法中調用其它方法的執行狀況,好比在execute方法中,想獲取add方法的執行耗時,必須把where設置成Where.AFTER.
    Kind.LINE:經過設置line,能夠監控代碼是否執行到指定的位置.
  下例定義監控bind()函數裏調用的全部其餘函數:

    @OnMethod(clazz = "java.net.ServerSocket", method = "bind", location = @Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/", where = Where.AFTER))
    public static void onBind(@Self Object self, @TargetInstance Object instance, @TargetMethodOrField String method, @Duration long duration)

  所調用的類及方法名所注入到@TargetInstance與 @TargetMethodOrField中。

  下例監控代碼是否到達了Socket類的第363行。

    @OnMethod(clazz = "java.net.ServerSocket", location = @Location(value = Kind.LINE, line = 363))

4. 如何使用Btrace定位問題

  1. 打印this,參數 與 返回值

  @OnMethod(clazz = "java.io.File", method = "createTempFile", location = @Location(value = Kind.RETURN))
  public static void o(@Self Object self, String prefix, String suffix, @Return AnyType result)

  若是想打印它們,首先按順序定義用@Self 註釋的this, 完整的參數列表,以及用@Return 註釋的返回值。

  須要打印哪一個就定義哪一個,不須要的就不要定義。但定義必定要按順序,好比參數列表不能跑到返回值的後面。

  Self:
    若是是靜態函數, self爲空。
    前面提到,若是上述使用了非JDK的類,命令行裏要指定classpath。不過,如前所述,由於BTrace裏不容許調用類的方法,因此定義具體類不少時候也沒意 思,因此self定義爲Object就夠了。
    參數數列表要麼不要定義,要定義就要定義完整,不然BTrace沒法處理不一樣參數的同名函數。
    用AnyType來定義任意類型的參數,相似於 Object 。
  2. 方法執行時,查看對象的實例屬性值
    再次強調,爲了保證性能不受影響,Btrace不容許調用任何實例方法。
    好比不能調用getter方法(怕在getter裏有複雜的計算),只會經過直接反射來讀取屬性名。
    又好比,除了JDK類,其餘類toString時只會打印其類名+System.IdentityHashCode。
    println, printArray,都按上面的規律進行,因此只能打打基本類型。
    若是想打印一個Object的屬性,用printFields()來反射。
    若是隻想反射某個屬性,參照下面打印Port屬性的寫法。從性能考慮,應把field用靜態變量緩存起來。
  注意JDK類與非JDK類的區別:

import java.lang.reflect.Field;
//JDK的類這樣寫就行
private static Field fdFiled = field("java.io,FileInputStream", "fd");
//非JDK的類,要給出ClassLoader,不然ClassNotFound
private static Field portField = field(classForName("com.vip.demo.MyObject", contextClassLoader()), "port");
public static void onChannelRead(@Self Object self) {
    println("port:" + getInt(portField, self));
}

 

  3.TLS,攔截函數間的通訊機制

  若是要多個攔截函數之間要通訊,可使用@TLS定義 ThreadLocal的變量來共享

@TLS
private static int port = -1;
@OnMethod(clazz = "java.net.ServerSocket", method = "<init>")
public static void onServerSocket(int p){
    port = p;
}
@OnMethod(clazz = "java.net.ServerSocket", method = "bind")
public static void onBind(){
  println("server socket at " + port);
}

  4. 誰調用了這個函數(原理:攔截到方法後,把堆棧打出來)

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

  5. 統計方法的調用次數,且每隔1分鐘打印調用次數

@Export static AtomicLong counter = new AtomicLong();
@OnMethod(class="com.**.MyObject",method="add")
public static void run(){
    counter.getAndIncrement();
}
@OnTimer(1000*60)
public static void run(){
    BTraceUtils.println("count: " + connter.get());
    counter.set(0);
}


5. linux 上使用 btrace!
  1. 下載壓btrace縮包: wget https://github.com/btraceio/btrace/releases/download/v1.3.11.3/btrace-bin-1.3.11.3.zip ;
  2. 解壓: unzip btrace-bin-1.3.11.3.zip -d btrace-bin-1.3.11.3;
  3. cd btrace-bin-1.3.11.3, ./btrace <pid> TracingScript.java, 就能夠看效果了;
  4. 修改腳本以解決問題;

  5. 經過反射機制,能夠很方法的獲得當前實例的屬性值;

        //print one field
        Field oneFiled = BTraceUtils.field("com.xx.test", "name");
        BTraceUtils.println("print one field: " + BTraceUtils.get(oneFiled, args[0]));

  6. BTraceUtils.printFields(args[1]); 調用封裝好的打印複雜對象;

            // 調用應用的各字段進行反射調用
            BTraceUtils.printFields(args[1]);

 

 

 

  注意: btrace 是字節碼注入,是可能致使jvm退出的,因此,應儘可能先在測試環境驗證ok後,再到線上調用,或者把注入的類範圍儘可能的縮小,而非大的正則匹配如: clazz="/com.xxx.*/", method="/.*/",就會致使大量的變動,影響性能也提升了運行風險!

  快速定位你的線上問題!

  最後,附上幾個調試過程的幾個經驗之坑:

    1. BTRACE_HOME 的設置,在 /etc/profile.d/btraceenv.sh 中設置便可; 若是不想設置BTRACE_HOME, 能夠直接切換到btrace的bin目錄操做便可;

    2. 使用 JVisualVm 能夠運行的trace代碼,不表明使用命令行也運行,最好都使用命令行驗證下腳本;

    3. JVisualVm 及一些jdk環境,支持 unsafe=true, 而在其餘環境則不必定容許; JVisualVm 中若是去除 unsafe=true, 則有些函數會受限制;

    4. btrace 腳本若是正常狀況沒法運行如: btrace 7311 TracingScriptTemplate.java, 則能夠切換到debug模式查看: btrace -v 7311 TracingScriptTemplate.java,這裏面會出現不少btrace的調試日誌,若是不想看這些日誌,能夠直接過濾掉: btrace -v 7311 TracingScriptTemplate.java | grep -v "DEBUG: ";

    5. 藉助IDE編寫的Trace腳本,能夠直接運行,即 trace 腳本可包含package包名,也能夠沒有包名;但文件必需要以.java結尾(沒有試過其餘小語種),不然編譯時會報錯;

    6. 代碼中若是自己就使用了一些開發腳本,如springboot的開發組件 spring-boot-devtools , 則可能致使 btrace 腳本沒法注入, 最好先去掉這些組件再上線;

 

最後,再附一個完整的鏈路監控的 script 供參考:

/* BTrace Script Template */

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

import static com.sun.btrace.BTraceUtils.currentThread;
import static com.sun.btrace.BTraceUtils.probeLine;
import static com.sun.btrace.BTraceUtils.threadId;

@BTrace
public class TracingScript {

    private static final String clazzForTracePattern = "/com.alipay.common.event.tbnotify.adapter.+/";
    private static final String methodForTracePattern = "/.*/";

    @OnMethod(
            clazz = clazzForTracePattern,
            method = methodForTracePattern,
            location = @Location(Kind.ENTRY)
    )
    public static void traceExecuteEnter(AnyType[] args, @ProbeClassName String name, @ProbeMethodName String method) {
        String output = "enter in: " + name + "." + method + "#" + probeLine() + ", ThreadId:" + threadId(currentThread());
        BTraceUtils.println(output);
        // 打印總體參數
        BTraceUtils.printArray(args);
        // 調用應用的各字段進行反射調用
        printPerFields(args);
        BTraceUtils.println("enter over...");
        // 結束符
        BTraceUtils.println("");
    }

    @OnMethod(
            clazz = clazzForTracePattern,
            method = methodForTracePattern,
            location = @Location(Kind.RETURN)
    )
    public static void traceExecuteReturn(AnyType[] args, @ProbeClassName String name, @ProbeMethodName String method, @Return AnyType result, @Duration long time) {
        long durationTime = time/1000000;
        if(durationTime > 0){
            String output = "return from: " + name + "." + method + "#" + probeLine() + " cost: " + durationTime + "ms, ThreadId:" + threadId(currentThread());
            BTraceUtils.println(output);
            // 打印總體參數
            BTraceUtils.printArray(args);
            printPerFields(args);
            BTraceUtils.printFields(result);
            BTraceUtils.println("return over...");
            // 結束符
            BTraceUtils.println("");
        }
    }

    // -----------------------------------------------------
    // =================  如下爲捕獲異常代碼  ================
    // -----------------------------------------------------
    @TLS
    private static Throwable currentException;

    @OnMethod(
            clazz = "java.lang.Throwable",
            method = "<init>"
    )
    public static void onthrow(@Self Throwable self) {
        currentException = self;
    }

    @OnMethod(
            clazz = "java.lang.Throwable",
            method = "<init>"
    )
    public static void onthrow1(@Self Throwable self, String s) {
        currentException = self;
    }

    @OnMethod(
            clazz = "java.lang.Throwable",
            method = "<init>"
    )
    public static void onthrow1(@Self Throwable self, String s, Throwable cause) {
        currentException = self;
    }

    @OnMethod(
            clazz = "java.lang.Throwable",
            method = "<init>"
    )
    public static void onthrow2(@Self Throwable self, Throwable cause) {
        currentException = self;
    }

    @OnMethod(
            clazz = "java.lang.Throwable",
            method = "<init>",
            location = @Location(Kind.RETURN)
    )
    public static void onThrownReturn() {
        if (currentException != null) {
            BTraceUtils.jstack(currentException);
            BTraceUtils.print("<--------------->");
            currentException = null;
        }
    }

    private static void printPerFields(AnyType[] args) {
        // 調用應用的各字段進行反射調用
        if(args.length > 0) {
            BTraceUtils.printFields(args[0]);
        }
        if(args.length >= 2) {
            BTraceUtils.printFields(args[1]);
        }
        if(args.length >= 3) {
            BTraceUtils.printFields(args[2]);
        }
    }

}
View Code
相關文章
相關標籤/搜索