ラベル trace の投稿を表示しています。 すべての投稿を表示
ラベル trace の投稿を表示しています。 すべての投稿を表示

2013/12/03

blktrace で block IO の分布を可視化する

はじめに

blktrace は block IO の発行をトレースする有名なツールです。blktrace で集めたトレースデータを解析する btt には、ディスク上での block IO の分布を可視化する bno_plot.py というツールが付属しています。bno_plot.py を使うと、gnuplot を用いて下記のような図を生成できます。


図では、x軸が時間、y軸が Block Number、z軸が Block per IO になっています。これから、いつ頃に、どのセクタに、どのくらいのIOが発行されたかがわかります。

使い方

bno_plot.py の使い方をご説明します。
blktrace と gnuplot をインストールします。
# yum -y install blktrace gnuplot

blktrace でトレースを収集します。
# blktrace -w 30 -d /dev/sda -o sda

収集したトレース情報を、btt で解析します。解析したデータをファイルに出力します。
# btt -i sda.blktrace.2 -B sda_cpu2
サフィックスが r.dat, w.dat, c.dat になっているファイルが生成されます。それぞれ、Read, Write, Read and Write データに対応します。

解析データを bno_plot.py に与え、gnuplot で図を描画します。
# bno_plot.py sda_cpu2_8,0_c.dat
これで、上記の画像のような図が得られます。

Block IO の分布を可視化すると、どのように負荷がかかっているのかわかりやすいです。
また、@akiradeveloper 氏が開発している、dm-writeboost のように、ランダムライトをシーケンシャルライトに変換する特殊なディスクキャッシュについて、IO分布を可視化すると特徴が見て取れて面白いかもしれません。

2013/12/6 追記:
早速 @akiradeveloper 氏がdm-writeboost の block IO 可視化をしてくれました!ありがとうございます。
テストステ論: (writeboost report) writeboostのI/Oを可視化した
予想通りdmへのIOがランダムでも、その下のデバイスへのIOはシーケンシャルになっている様子が見て取れます。

2013/11/27

Erlang/OTP の systemtap トレース機能の使い方

はじめに

Erlang/OTP には systemtap と連携したトレース機能が備わっています。Erlang/OTP で systemtap 連携を有効にすると、systemtap から Erlang のトレースポイントを扱うことができます。トレースポイントを使うことで、BEAM 仮想マシンの挙動をより正確に把握したり、性能解析しやすくなります。

使い方

Erlang/OTP の ビルド

systemtap 連携を使うには、Erlang/OTP を configure --with-dynamic-trace=systemtap でビルドする必要があります。
# git clone git://github.com/erlang/otp.git
# cd otp
# ./otp_build autoconf
# ./configure --with-dynamic-trace=systemtap
# make

Erlang Shell を起動して [systemtap] と表示されていれば、systemtap が有効になっていることが確認できます。
# ./bin/erl
Erlang R16B03 (erts-5.10.4) [source-fb0006c] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [systemtap]

Eshell V5.10.4  (abort with ^G)

stap スクリプト起動方法

まず、stap -L コマンドで トレースポイントを確認しましょう。予め、beam バイナリファイルへの PATH を通しておいてください。
# PATH=/home/eiichi/git/otp/bin/x86_64-unknown-linux-gnu/:$PATH stap -L 'process("beam.smp").mark("*")' 
process("beam.smp").mark("aio_pool__add") $arg1:long $arg2:long
process("beam.smp").mark("aio_pool__get") $arg1:long $arg2:long
process("beam.smp").mark("bif__entry") $arg1:long $arg2:long
process("beam.smp").mark("bif__return") $arg1:long $arg2:long
process("beam.smp").mark("copy__object") $arg1:long $arg2:long
process("beam.smp").mark("copy__struct") $arg1:long
process("beam.smp").mark("dist__monitor") $arg1:long $arg2:long $arg3:long $arg4:long $arg5:long
process("beam.smp").mark("dist__output") $arg1:long $arg2:long $arg3:long $arg4:long
process("beam.smp").mark("dist__outputv") $arg1:long $arg2:long $arg3:long $arg4:long
process("beam.smp").mark("dist__port_busy") $arg1:long $arg2:long $arg3:long $arg4:long
...

約60個のトレースポイント(user-probe 系を除く)が確認できます。Linux Kernel のトレースポイントは 約 1200個、Qemu のトレースポイントが 約 900個なのと比較すると、若干少ないですね。

これらトレースポイントのうち、gc_major_start を例にトレースポイントの使い方を説明します。

まず、下記のような stap スクリプトを用意しましょう。(otp/lib/runtime_tools/examples より抜粋)
# cat garbage-collection.systemtap 
probe process("beam.smp").mark("gc_major-start")
{
    printf("GC major start pid %s need %d words\n", user_string($arg1), $arg2);
}

systemtap を有効にした Erlang Shell を起動し、下記のコマンドでsystemtap スクリプトを起動します。
# PATH=/home/eiichi/git/otp/bin/x86_64-unknown-linux-gnu/:$PATH stap garbage-collection.systemtap                    
Erlang Shell で適当に > "aaaaaaaaaa". などとコマンドを入力すると、stap スクリプトで下記のような出力が得られます。
GC major start pid <0 .33.0=""> need 9 words

GCの他にも、Message の送受信、プロセスの spawn、スケジュールなど、興味深く有用なトレースポイントがありあますので、お試しください。
systemtap スクリプトの例は lib/runtime_tools/example にあります。

dyntrace(user-probe)

Erlang/OTP で systemtap 連携を有効にすると、dyntrace モジュールを使って、 Erlang コードから動的にsystemtap スクリプトに情報を出力することができます。
Erlang/OTP に同伴されている、lib/runtime_tools/example/user-probe.systemtap を例にとって dyntrace モジュールの使いかたを説明します。

まず、user-probe.systemtap は beam 向けになっていますので、beam.smp 向けに直しましょう。user-probe.systemtap を開き、process('beam') となっているところを process('beam.smp') に書き換えます。
# vim user-probe.systemtap

書き換えたら、Erlang Shell を起動し、user-probe.systemtap を起動します。(beam に PATHを通しておいてください。)
# stap user-proeb.systemtap

この状態で、Erlang Shell にて、以下のように入力します。
2> dyntrace:p(1, 2, 3, 4, "a", "b", "c").
true
3> dyntrace:put_tag("test").
undefined
4> dyntrace:p(1, 2, 3, 4, "a", "b", "c").
true

すると、stap スクリプトでは、以下のような出力が得られます。
<0 .33.0="">  1 2 3 4 'a' 'b' 'c' 'c'
<0 .33.0=""> test 1 2 3 4 'a' 'b' 'c' 'c'

dyntrace:p/nで各種情報を出力します。dyntrace:put_tag/1 でトレース出力のプレフィックスを設定することができます。
詳しくはErlang User's Guide: dyntrace をご覧ください。

参考文献

Dtrace and Erlang: a new beginning
Erlang User's Guide: Systemtap and Erlang/OTP
Erlang User's Guide: dyntrace
Systemtap and Erlang: a tutorial
runtime_tools/src/dyntrace.erl

2013/05/09

Qemu のトレース新機能 "ftrace backend" 紹介

はじめに

Qemu のトレース新機能 "ftace backend" は Linux 標準のトレース機構 ftrace を使って Qemu と Linux Kernel(KVM) のトレース情報を併せて取得する機能です。Qemu で KVM を使う場合は、ユーザ空間(Qemu)とカーネル空間(Kernel)を頻繁に遷移するため、両空間のトレース情報を併せて取得できると、デバッグや性能解析がよりはかどります。

ftrace backend の実装には ftrace marker が用いられています。ftrace marker は debugfs の marker file への書き込みを ftrace のリングバッファに送る機能です。Qemu ftrace backend は、Qemu のトレース情報出力先を marker file にすることで実現しています。

関連コミット:
trace: Add ftrace tracing backend

2013年5月3日に Qemu Mainline にマージされました。おそらく Qemu 1.5 で使用できるはずです。Author の名前が自分と酷似していますが。。。

使い方

ftrace backend を使うためには、まず configure 時に trace backend として "ftrace" を指定する必要があります。
# ./configure --trace-backend=ftrace

このままでも ftrace backend はトレース情報を debugfs の trace file に記録できますが、今回は KVM を使うのでKVM 関係のトレースイベント情報も併せて取得するよう設定します。
# echo 1 > /sys/kernel/debug/tracing/events/kvm/enable

Qemu 起動時に、取得対象の Qemu trace イベントを指定します。
Qemu のトレースイベント一覧は Qemu ソースコードの trace-events ファイルに記載されています。また、 Qemu monitor から "info trace-events" コマンドによっても取得できます。
ここでは、すべてのイベントを取得するような設定にします。
% cat /home/eiichi/events 
*
Qemu を起動します。ftrace を使うため、必ず root 権限で起動しましょう。
# ./qemu-system-x86_64 -enable-kvm -trace events=/home/eiichi/events

得られるトレース出力は以下のようになります。
 # less /sys/kernel/debug/tracing/trace
snip...
 qemu-system-x86-23226 [002] d... 116142.685922: kvm_entry: vcpu 0
 qemu-system-x86-23226 [002] d... 116142.685923: kvm_exit: reason IO_INSTRUCTION rip 0xc45b info 700040 0
 qemu-system-x86-23226 [002] .... 116142.685924: kvm_pio: pio_write at 0x70 size 1 count 1
 qemu-system-x86-23226 [002] .... 116142.685925: kvm_userspace_exit: reason KVM_EXIT_IO (2)
 qemu-system-x86-23226 [002] ...1 116142.685943: tracing_mark_write: cpu_set_apic_base 00000000fee00900
 qemu-system-x86-23226 [002] ...1 116142.685946: tracing_mark_write: kvm_run_exit cpu_index 0, reason 2
 qemu-system-x86-23226 [002] ...1 116142.685947: tracing_mark_write: cpu_out addr 0x70 value 143
 qemu-system-x86-23226 [002] ...1 116142.685951: tracing_mark_write: kvm_vcpu_ioctl cpu_index 0, type 44672, arg (nil)
 qemu-system-x86-23226 [002] d... 116142.685954: kvm_entry: vcpu 0
snip...
tracing_mark_write と書かれているのが、Qemu(ユーザ空間)のトレース情報です。ここでは、ゲストCPUのIO port write命令を受けて VM_EXIT(reason KVM_EXIT_IO) したのちユーザ空間でエミュレーションが行われているのがわかります。

libvirt で使う場合

libvirt で ftrace backend を使う場合は、libvirtd の設定ファイルを以下のように変更し、Qemu を起動するユーザを root にする必要があります。
/etc/libvirt/qemu.conf に以下を追加:
user = "root"

 余談

ftrace marker を使うためのライブラリがあれば、もっとftrace backend のコードを短くできますし、他のアプリケーションでも手軽に ftrace marker が使えるようになって便利です。ライブラリ名は、"libftrace" などという名前になるのでしょうか。
最近、ftrace は snapshot 機能や、multiple buffer 機能が追加されています。ユーザ空間のプログラム内からそれらの機能を利用するライブラリがあるとさらに嬉しいです。

2013/03/24

ftrace marker と uprobe-based event tracer オーバヘッド比較

はじめに

ユーザ空間のプログラムのトレース情報を取得する方法には、さまざまなものがありますが、ここでは "ftrace marker" と "uprobe-based event tracer" を取り上げ、トレースオーバヘッドを測定してみます。
"ftrace marker" とは ftrace trace_marker ファイルにトレース情報を書き込むことで、ftrace でトレース情報を取得する機能です。Kernel のトレース情報とユーザプログラムのトレース情報を照らしあわせたい時等に便利です。
使い方は簡単で、tracing ディレクトリの trace_marker ファイルに write するだけです。

# echo test > /sys/kernel/debug/tracing/ftrace_marker
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1   #P:4
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
           <...>-23108 [003] ...1 34500.124363: tracing_mark_write: test

"uprobe-based event tracer" とは kprobe-based event tracer のユーザ空間版で、kprobe のように動的にプローブを差し込むことができる機能です。以下でも若干解説しますが、詳しい使い方については、ドキュメントをご覧ください。

比較方法
"ftrace marker" と "uprobe-based event tracer" をそれぞれ使って、トレースポイントを 1000回踏むのにかかる時間を測定します。
測定プログラムは以下を用意しました。
下の3つの関数をそれぞれ 1000回ずつ呼び出し、かかった時間を測定する簡単なものです。
do_nothing()  : 関数呼び出しのオーバヘッドを考慮するための何もしない関数
marker_write() : ftrace marker に write する関数
uprobe_test() :  uprobe を差し込む対象の関数

probench.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/time.h>

int marker_fd;

long time_usec(void) {
    struct timeval tv;

    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000 + tv.tv_usec;
}

void probe_bench(void (*func)()) {
    long start_us, end_us, diff_us;
    int i, loop_max;

    loop_max = 1000;
    start_us = time_usec();
    for (i = 0; i < loop_max; i++) {
        func();
    }
    end_us = time_usec();
    diff_us = end_us - start_us;
    printf("Elapsed time(us): %ld\n", diff_us);
    printf("per trace point(us): %f\n", (double) diff_us / loop_max);
    printf("\n");
}

void do_nothing(void) {
}

void marker_write(void) {
    write(marker_fd, "test\n", 5);
}

void uprobe_test(void) {
}


int main(int argc, char *argv)
{
    char path[256];
    int trace_fd;

    strcpy(path, "/sys/kernel/debug/tracing/tracing_on");
    trace_fd = open(path, O_WRONLY);
    if (trace_fd >= 0)
        write(trace_fd, "1", 1);

    strcpy(path, "/sys/kernel/debug/tracing/trace_marker");
    marker_fd = open(path, O_WRONLY);

    printf("-- nothing --\n");
    probe_bench(do_nothing);

    printf("-- ftrace marker --\n");
    probe_bench(marker_write);

    printf("-- uprobe --\n");
    probe_bench(uprobe_test);

    close(marker_fd);
}
Makefile
probench: probench.c
 gcc -g -o $@ $< -O2

clean:
 rm probench

上記のプログラムを make したあと、uprobe を差し込む場所を指定します。
まず、ELF を読んでプログラムヘッダからテキストセグメント開始アドレスを探しましょう。
# readelf -e probench | less
...
プログラムヘッダ:
  タイプ        オフセット          仮想Addr           物理Addr
            ファイルサイズ        メモリサイズ         フラグ 整列
...
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000be4 0x0000000000000be4  R E    200000
テキストセグメント開始アドレスは0x400000とわかりました。
次に、uprobe_test 関数のアドレスは以下のコマンドで取得できます。
 # readelf -s probench | grep uprobe_test
    59: 0000000000400830     2 FUNC    GLOBAL DEFAULT   13 uprobe_test
アドレスは0x400830とわかりました。
uprobe_test にプローブを差し込みましょう。uprobe_test 関数のアドレス0x400830 から テキストセグメント開始アドレス 0x400000を引き算した 0x830 をuprobe_events に書き込みます。書き込み後はイベントをオンにしてやります。
# echo 'p:test /path/to/probench:0x830' > /sys/kerenel/debug/tracing/uprobe_events
# echo 1 > /sys/kerenel/debug/tracing/events/uprobes/test/enable
以上で準備がととのいました。ベンチマークプログラム : probench を実行しましょう。
# ./probench       
-- nothing --
Elapsed time(us): 8
per trace point(us): 0.008000

-- ftrace marker --
Elapsed time(us): 1742
per trace point(us): 1.742000

-- uprobe --
Elapsed time(us): 4255
per trace point(us): 4.255000

結果は、
ftrace marker : 1.7us/TracePoint
uprobe : 4.3us/TracePoint
でした。関数呼び出しのオーバヘッドは無視できるようです。
uprobe は ftrace marker と比べて、若干オーバヘッドが嵩むようです。

余談
uprobe-based event tracer ではなく、systemtap を使った場合でも測って見ました。
以下のようなスクリプトを実行したあとで probench を走らせます。
#!/usr/bin/stap

probe begin {
 printf("start\n")
}

probe process("/home/eiichi/git/probench/probench").function("uprobe_test") {
}

# ./probench
-- nothing --
Elapsed time(us): 8
per trace point(us): 0.008000

-- ftrace marker --
Elapsed time(us): 1766
per trace point(us): 1.766000

-- uprobe --
Elapsed time(us): 3994
per trace point(us): 3.994000
systemtap の場合は 4.0us/TracePoint でした。uprobe-based event tracer と変わらないようです。

まとめ
"ftrace marker" と "uprobe-based event tracer" のオーバヘッドを比較すると、前者より後者のほうが2倍以上大きいことがわかりました。トレースオーバヘッドを気にする かつ ユーザプログラムを改変できる場合は ftrace marker を使ったほうがいいでしょう。

参考情報
kernel/Documentation/trace/ftrace.txt
kernel/Documentation/trace/kprobetracer.txt
kernel/Documentation/trace/uprobetracer.txt

2013/02/02

手軽にqemuのトレースを採る

qemu内部のロジックを追ったり、性能解析をするにはトレース採取が有効です。qemuのトレースを手軽に採る方法についての投稿です。

qemuのトレースを採る方法については、いくつかありますが、標準エラー出力にトレースを書き出す "stderr" trace-backend がもっとも簡単な方法です。
"stderr" backend の仕組みは qemu 内部のトレースイベントを標準エラー出力にデバッグprintfをするという非常に単純なものです。
qemu-kvm 0.15よりサポートされています。

関連コミット:
http://git.qemu.org/?p=qemu.git;a=commit;h=320fba2a1f384e17db150d74540a2cf005eb47b5


使い方
qemuのトレースは採取しない設定でもオーバヘッドがあるため、デフォルトではオフになっています。トレースを有効にするには、コンパイル時にトレースを有効にする必要があります。
コンパイルオプションは以下のようになります。
./configure --prefix=/usr/local/ --enable-trace-backend=stderr

トレースイベント一覧は以下のファイルにかかれています。
qemu/trace-events

イベント一覧から、取得したいイベントを選びファイルに書き出します。 例えば、以下のような内容のファイルを作ります。ワイルドカード指定が可能です。
g_malloc
g_free
qemu*
virtio*

qemu を起動する際に、このイベントファイルを-traceオプションに指定します。
sudo ./qemu-system-x86_64 -enable-kvm -hda ~/vmimg/f1.img -m 512 -trace events=/path/to/events-file

得られる出力は以下のようになります。
g_malloc size 16 ptr 0x7f3f40004270
qemu_co_mutex_lock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_lock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_coroutine_yield from 0x7f3f54a7faa0 to 0x7f3f400009d8
qemu_coroutine_enter from 0x7f3f54a7fbb8 to 0x7f3f54a7faa0 opaque (nil)
qemu_co_mutex_lock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_lock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
g_free ptr 0x7f3f40004270


libvirtとの連携
qemuを利用する際は、通常libvirt経由で利用することが多いと思います。ここではlibvirtから"stderr" trace-backend を利用する際のtipsを記載します。

・libvirtのログに出力
virsh start domain コマンドでドメインを起動した場合、stderrの出力先は、以下になります。
/var/log/libvirt/qemu/domain.log

・動的なイベントのオン・オフ
qemu monitor command を利用することで、稼働中のドメインで採取するイベントを動的にon/offできます。

利用可能なトレースイベント一覧の取得
# virsh qemu-monitor-command --hmp domain info trace-events

イベントのon・off
# virsh qemu-monitor-command --hmp domain trace-event qemu_vmalloc on
# virsh qemu-monitor-command --hmp domain trace-event qemu_vmalloc off
参考情報

Observability using QEMU tracing
qemuのtrace機能
docs/tracing