ぺーぺーSEのブログ

備忘録・メモ用サイト。

JavaアプリHotトレーサー「BTrace」

BTraceは起動中のJavaアプリケーションのトレース情報を取得できる便利なツール

公式:
http://kenai.com/projects/btrace/

Developer's Guide:
http://kenai.com/projects/btrace/pages/DeveloperGuide

使い方

上記から入手したbin内にbtraceというシェルがある。(Windowsはbat)

> bin/btrace [PID] [トレース用のJavaソース]

PIDはjpsで取得。
実行ユーザはトレース対象のJavaプロセスの実行ユーザと同じ。
トレース用のJavaソースはコンパイル不要!

ソースの書き方

制約
  • BTraceUtilsで定義されているメソッドしか使えない。
  • 任意のクラスをnewしたりStringクラスのメソッドを使うこともできない。
アノテーション
◇クラスに適用するアノテーション
@BTrace
	クラスの前に定義

◇メインメソッドに適用するアノテーション
@OnMethod
	clazz:トレースしたいクラス名。正規表現の場合は/で囲む。
		(例)clazz="/java\.io\..*/"
	method:トレースしたいメソッド名。正規表現の場合は/で囲む。
		(例)clazz="/.*/"
	location:@Locationを指定
	@Location
		value:トレースを適用するタイミング。メソッド呼び出し時はKind.CALL
		clazz:トレースを適用するクラス。
		method:トレースを適用するメソッド。
		(例)location=@Location(Kind.CALL, clazz="/.*/", method="/.*/")
@OnTimer
	指定されたミリ秒置きにトレース
		(例)@OnTimer(4000)

◇メインメソッドのパラメータに適用するアノテーション
@Self
	トレースしているオブジェクト
@probeClassName
	トレースしているクラス名
@probeMethodName
	トレースしているメソッド名
@TargetInstance
	トレースしているメソッド内で呼ばれているオブジェクト
@TargetMethodOrField
	トレースしているメソッド内で呼ばれているオブジェクトのメソッド
@アノテーションなし
	トレースしているメソッド内で呼ばれているオブジェクトのメソッドのパラメータが入るみたい


org.sampleパッケージで呼び出されているメソッドのトレース
package sample;

import static com.sun.btrace.BTraceUtils.currentThread;
import static com.sun.btrace.BTraceUtils.name;
import static com.sun.btrace.BTraceUtils.println;
import static com.sun.btrace.BTraceUtils.str;
import static com.sun.btrace.BTraceUtils.timestamp;

import com.sun.btrace.BTraceUtils.Strings;
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.Self;
import com.sun.btrace.annotations.TargetInstance;
import com.sun.btrace.annotations.TargetMethodOrField;

@BTrace public class OnCall {
    @OnMethod(
        clazz="/org\\.sample\\..*/",
        method="/.*/",
        location=@Location(value=Kind.CALL, clazz="/.*/", method="/.*/")
    )
    public static void m(
            @Self Object self,
            @ProbeClassName String probeClass,
            @ProbeMethodName String probeMethod,
            @TargetInstance Object instance,
            @TargetMethodOrField String method,
            String text) {
        String msg = "";
        msg = Strings.concat(msg, timestamp("yyyy/MM/dd HH:mm:ss.SSS"));
        msg = Strings.concat(msg, " ");
        msg = Strings.concat(msg, name(currentThread()));
        msg = Strings.concat(msg, " [");
        msg = Strings.concat(msg, str(self));
        msg = Strings.concat(msg, "#");
        msg = Strings.concat(msg, probeMethod);
        msg = Strings.concat(msg, " -> ");
        msg = Strings.concat(msg, str(instance));
        msg = Strings.concat(msg, "#");
        msg = Strings.concat(msg, method);
        msg = Strings.concat(msg, "(\"");
        msg = Strings.concat(msg, text);
        msg = Strings.concat(msg, "\")");
        msg = Strings.concat(msg, "]");

        println(msg);
    }
}


org.sampleパッケージで実行されているメソッドのトレース
package sample;

import static com.sun.btrace.BTraceUtils.currentThread;
import static com.sun.btrace.BTraceUtils.name;
import static com.sun.btrace.BTraceUtils.println;
import static com.sun.btrace.BTraceUtils.str;
import static com.sun.btrace.BTraceUtils.timestamp;

import com.sun.btrace.BTraceUtils.Strings;
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 com.sun.btrace.annotations.Self;

@BTrace public class OnMethods {
    @OnMethod(
        clazz="/org\\.sample\\..*/",
        method="/.*/"
    )
    public static void m(
            @Self Object self,
            @ProbeClassName String probeClass,
            @ProbeMethodName String probeMethod
            ) {

        String msg = "";
        msg = Strings.concat(msg, timestamp("yyyy/MM/dd HH:mm:ss.SSS"));
        msg = Strings.concat(msg, " ");
        msg = Strings.concat(msg, name(currentThread()));
        msg = Strings.concat(msg, " [");
        msg = Strings.concat(msg, str(self));
        msg = Strings.concat(msg, "#");
        msg = Strings.concat(msg, probeMethod);
        msg = Strings.concat(msg, "]");

        println(msg);
    }
}


例外発生時にトレース
package sample;

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

@BTrace public class OnThrow {    

    @TLS 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 onthrowreturn() {
        if (currentException != null) {
            Threads.jstack(currentException);
            println("=====================");
            currentException = null;
        }
    }
}


スレッド生成時にトレース
package sample;

import static com.sun.btrace.BTraceUtils.name;
import static com.sun.btrace.BTraceUtils.println;
import static com.sun.btrace.BTraceUtils.timestamp;

import com.sun.btrace.BTraceUtils.Strings;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.Self;

@BTrace public class ThreadStart {
    @OnMethod(
        clazz="java.lang.Thread",
        method="start"
    )
    public static void onnewThread(@Self Thread t) {
//        D.probe("jthreadstart", Threads.name(t));
        String msg = "";
        msg = Strings.concat(msg, timestamp("yyyy/MM/dd HH:mm:ss.SSS"));
        msg = Strings.concat(msg, " starting [");
        msg = Strings.concat(msg, name(t));
        msg = Strings.concat(msg, "]");

        println(msg);

    }
}


スレッドのスタックトレース
package sample;

import static com.sun.btrace.BTraceUtils.exit;
import static com.sun.btrace.BTraceUtils.Threads.deadlocks;
import static com.sun.btrace.BTraceUtils.Threads.jstackAll;

import com.sun.btrace.annotations.BTrace;

@BTrace
public class JStack {
    static {
        deadlocks(false);
        jstackAll();
        exit(0);
    }
}


ヒープダンプの取得

※出力先は指定できない。Javaのカレントディレクトリに出力される。
 JavaのSystem.Propertiesの"user.dir"を参照。

package sample;

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

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

@BTrace
public class HeapDump {
    static {
        String name;
        if (Sys.$length() == 3) {
            name = Sys.$(2);
        } else {
            name = "heap.bin";
        }
        Sys.Memory.dumpHeap(name);

        print("heap dumped! > ");
        print(Sys.Env.property("user.dir"));
        print(Sys.Env.property("file.separator"));
        print("btrace");
        print(Sys.$(0));
        println();
        Sys.exit(0);
    }
}


デッドロックの検出
package sample;

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

@BTrace public class Deadlock {
    @OnTimer(4000)
    public static void print() {
        deadlocks();        
    }
}

参考:
http://d.hatena.ne.jp/torutk/20100930/p1
https://blogs.oracle.com/okazaki/entry/%E3%83%AF%E3%82%AF%E3%83%AF%E3%82%AF%E6%8A%80%E8%A1%93_btrace
http://gihyo.jp/dev/clip/01/orangenews/vol48/0004