ftraceの仕組みとアーキテクチャ

はじめに

じっくりとライトブーストの仕事をする予定だったのに, ライトブーストのテストにはsystemtapを使いたいなぁとか妄想してたら, 気づいたらftraceについて調べていたので一段落ということでメモっておく. 参考資料としては, “Linuxカーネル Hacks"の8章と, カーネルのソースコードと, ネット上に転がっている文書もろもろ.

理解した内容については, Twitterで散々つぶやくも片っ端から無視されてしまったので, 完全に独断によるものだから(想像も多く含まれる), 正しいかどうか分からないが, 誤っていたら/補足があれば, 今度こそTwitterで指摘して.

色々調べてしまったので, 検討の結果, いくつかの記事に分離して書くことにした. 2-3個になると思う.

ftraceの基本原理

ftraceの仕事は, 名前の示すとおり, 関数単位である. 関数単位で, 「それが実行された時刻」「どこから呼ばれたか」などを記録して, traceというファイルとして表示することが主な仕事である.

ftraceの仕組み

そのために, ftraceは, 関数の先頭にフックを埋め込む. これをmcountという. mcountは, gccの-pgフラグを指定することで埋め込むことが出来る.

1
 -pg Generate extra code to write profile information suitable for the analysis program gprof.  You must use this option when compiling the source files you want data about, and you must also use it when linking. 

mcountはアーキテクチャごとに(そのもの, あるいはarch-specificなフックを標準的なmcountの中に)実装可能である(この関数には, 「どこから呼ばれたか」「どこに返るのか」という情報が渡される). ftraceは, この中で. 自分のフックに飛んだあとは, 自分の庭の中で, 時間の取得やプラグイントレーサの実行など好き勝手やる.

mcountからのftraceへのジャンプはここを参考した. http://d.hatena.ne.jp/akachochin/20101027/1288185836

イベント

関数の実行以外に, コードに埋め込まれたトレースポイントも統合して見ることが出来る. これは, register_traceというマクロによって表現されていて, プローブが埋め込まれる. イベント駆動でそのプローブがコールされ, あとはトレースポイントに定義された動作を行う.

1
2
3
4
5
        register_trace_##name(void (*probe)(data_proto), void *data)    \
        {                                                               \
                return tracepoint_probe_register(#name, (void *)probe,  \
                                                 data);                 \
        }

リングバッファ

イベントが補足されるとプローブがコールされ, 例えばblktraceの場合は, __blk_add_traceに行き着く. ここではring_buffer_event_dataなる関数がコールされている. リングバッファはコアごとに用意され, ftraceとevent traceの両方のデータがどんどん流し込まれる. このデータはどれも時間を持っているため, 最終的に, 時間でソートすることによって全コアのトレースデータを統合することが可能となる.

フィルタ

ftraceは, filterという概念を多用している.

filterは二枚ある. 1枚は, 「どの関数をトレースするか」であり, もう1枚は「どのイベントをトレースするか」である. この2つに階層関係があるかどうかはよく分からなかった(あるとしたら, トレースする関数から呼び出されたものしかトレースするイベントを取得しないとかいうことになるが, そこまではやるべきではないと思う)が, たぶんないと思う. なぜならば意図と違うし, イベントを除外してしまうとそのイベントを使っている他の機能が困る可能性があるから. ただし, ftraceのプラグイントレーサが, 自分に関係のないeventを排除している可能性は残る.

プラグイントレーサ

自分の庭では, 「主なトレーサ」であるプラグイントレーサと「副なトレーサ」であるevent tracer, stack tracerが動くことが出来る. 主なトレーサは1つ指定出来ない. これはavailable_tracersにリストがある. これをcurrent_tracerに代入することで有効となる.

プラグイントレーサは, struct tracerで実装する. これがプラグインたる所以である. 例えば, blktraceについては以下のように定義してある. 主な仕事は「そのトレーサに必要なデータの確保」「出力方法の定義」である. データを拾うためのフックは見つからなかった(自分独自のデータを集めても良いと思うのだが). だとすると, データを集める方法はftraceに一任されていることになる(残念ながら, データの集め方はよく分からなかった. ftrace_event_class, ftrace_opsが怪しいかと思ったが). この標準的なトレース情報をいかに整形して出力するかがトレーサの個性といえる.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static struct tracer blk_tracer __read_mostly = {
        .name           = "blk",
        .init           = blk_tracer_init,
        .reset          = blk_tracer_reset,
        .start          = blk_tracer_start,
        .stop           = blk_tracer_stop,
        .print_header   = blk_tracer_print_header,
        .print_line     = blk_tracer_print_line,
        .flags          = &blk_tracer_flags,
        .set_flag       = blk_tracer_set_flag,
};
comments powered by Disqus
Built with Hugo
テーマ StackJimmy によって設計されています。