DTrace の組み込み書式設定関数には、printf() と printa() があります。D プログラム内でこれらの関数を使用することにより、出力書式を設定できます。D コンパイラには、printf(3C) ライブラリルーチンには含まれない機能があるため、printf() の知識をお持ちのユーザーも、必ずこの章に目を通してください。この章では、trace() 関数の書式設定処理と、dtrace(1M) で集積体を表示するときのデフォルトの出力書式についても説明します。
printf() 関数は、trace() 関数のようにデータをトレースする機能と、データとその他のテキストを指定された書式で出力する機能を兼ね備えています。DTrace は、printf() 関数からの指示を受けて、2 番目以降の各引数に関連付けられたデータをトレースし、最初の printf() 引数 (「書式設定文字列」) で指定された規則に従って結果を書式設定します。
書式設定文字列は、% で始まる任意の数の書式変換を含む標準文字列で、対応する引数の書式を指定します。この例の書式設定文字列の最初の変換は 2 番目の printf() 引数、2 番目の変換は 3 番目の引数 (以降同様) に対応しています。変換と変換の間のすべてのテキストは、変更されずそのまま出力されます。% 変換文字に続く文字は、対応する引数の書式を表します。
DTrace の printf() は、D コンパイラが認識する組み込み関数です。この点で、printf(3C) とは異なります。D コンパイラは、C ライブラリの printf() にはない、次のような便利なサービスを、DTrace の printf() に提供します。
D コンパイラは、書式設定文字列内の変換の引数を比較します。引数の型と書式変換に互換性がない場合、D コンパイラはエラーメッセージを発行し、問題について報告します。
D コンパイラは、printf() 書式変換でのサイズ接頭辞の使用を要求しません。C の printf() ルーチンは、引数のサイズの指定を要求します。したがって、%ld (long の場合)、%lld (long long の場合) などの接頭辞を追加する必要があります。D の printf() 文では、これらの接頭辞は不要です。D コンパイラは、引数のサイズと型を識別できます。
DTrace では、デバッグや監視に役立つ追加の書式文字を使用できます。たとえば、書式変換 %a を使って、ポインタのシンボル名とオフセットを出力できます。
この機能を利用するためには、D プログラム内で、DTrace の printf() 関数の書式設定文字列を、文字列定数として指定しなければなりません。string 型の動的変数は、書式設定文字列として使用できません。
書式設定文字列内の変換指定は、パーセント記号 (%) で始まります。その後、順番に次の情報を指定します。
変換指定の意味を変更する 0 個以上の「フラグ」。複数指定する場合、どのような順序で指定してもかまいません。詳細は、次の節で説明します。
最小「フィールド幅」(オプション)。変換後の値のバイト数が、フィールド幅のバイト数より少ない場合、デフォルトで、値の左側に空白文字が入ります。左詰めフラグ (-) が指定されている場合、空白文字は値の右側に入ります。フィールド幅は、アスタリスク (*) でも指定できます。この場合、フィールド幅は、int 型の追加引数の値に基づいて動的に設定されます。
「精度」(オプション)。変換が d、i、o、u、x、および X の場合、表示される最小桁数 (フィールドの先頭にはゼロが入る) を表します。変換が e、E、および f の場合は基数文字の後ろに表示される桁数、変換が g または G の場合は最大有効桁数、変換が s の場合は最大バイト数を表します。精度は、ピリオド (.) とアスタリスク (*)、またはピリオドと 10 進数の文字列で指定します。
「サイズ接頭辞」(オプション)。対応する引数のサイズを指定します。詳細については、「サイズ接頭辞」を参照してください。D ではサイズ接頭辞を指定する必要はありませんが、C の printf() 関数との互換性のため、使用できるようになっています。
「変換指定子」。引数に適用する変換の型を指定します。
printf(3C) 関数では % n$ (n は 10 進整数) の形式も使用できますが、DTrace の printf() では、この形式の変換指定はサポートされていません。
printf() の変換フラグを有効にするには、次の文字を 1 つ以上指定します。複数指定する場合、どのような順序で指定してもかまいません。
10 進変換 (%i、%d、%u、%f、%g、%G) の結果の整数部分の書式は、通貨文字を除くさまざまなグループ化文字を使って設定できます。POSIX の C ロケールを含むいくつかのロケールでは、このフラグと併用できる通貨文字以外のグループ化文字は提供されていません。
変換の結果は、左詰めになります。このフラグを指定しなければ、右詰めになります。
符号付き変換の結果に、常に符号 (+ または -) を付けます。このフラグを指定しなければ、負の値の変換のときだけ符号が付きます。
符号付き変換の先頭文字が符号でない場合や、符号付き変換の結果、文字がなくなった場合、結果の前に空白文字を挿入します。space フラグと + フラグの両方が指定された場合、space フラグは無視されます。
選択された変換に代替書式が定義されている場合、変換後の値をこの代替書式で表します。変換の代替書式の説明は、変換ごとに記載します。
変換が d、i、o、u、x、X、e、E、f、g、および G の場合、符号や基数のあと、フィールド幅に合わせて、先頭にゼロを挿入します。空白文字によるパディングは行いません。0 フラグと - フラグの両方が指定された場合、0 フラグは無視されます。変換が d、i、o、u、x、および X で、精度が指定されている場合、0 フラグは無視されます。0 フラグと ' フラグの両方が指定された場合、ゼロによるパディングの前にグループ化文字が挿入されます。
最小フィールド幅は、任意のフラグ指定子と 10 進文字列の組み合わせで指定できます。この場合、フィールド幅は、指定された桁数になります。フィールド幅は、アスタリスク (*) でも指定できます。この場合、フィールド幅は、int 型の追加引数によって決定されます。たとえば、int 変数 w の値からフィールド幅を決定し、このフィールド幅で整数 x を出力する場合、次のような D 文を使用します。
printf("%*d", w, x);
フィールド幅は、疑問符 (?) でも指定できます。この場合は、オペレーティングシステムカーネルのデータモデル内で、アドレスを 16 進値で表すために必要な文字数に基づいて、フィールド幅が決定されます。たとえば、カーネルが使用しているデータモデルが 32 ビットの場合は 8、64 ビットの場合は 16 になります。
変換の精度は、ピリオド (.) に続く 10 進文字列、またはピリオドに続くアスタリスク (*) で指定できます。アスタリスクを使って精度を指定した場合、変換引数の前に追加された int 型変数によって精度が決定されます。フィールド幅と精度の両方をアスタリスクで指定した場合、printf() の引数は、幅、精度、値の順で指定する必要があります。
printf(3C) を使用する ANSI-C プログラムでは、変換引数のサイズと型を指定するため、必ずサイズ接頭辞を使用しなければなりません。D プログラムでは、サイズ接頭辞は不要です。これは、D コンパイラが、printf() 呼び出しごとに変換引数とサイズと型を自動的に指定するためです。D プログラムでも、C との互換性を確保するため、サイズ接頭辞を使用することは可能ですが、なるべく使用しないでください。サイズ接頭辞を使用すると、派生型を使用するとき、コードが特定のデータモデルに結合されてしまいます。たとえば、typedef を、データモデルに基づいて別の整数基本型として再定義する場合、最初のデータモデルと再定義後のデータモデルの両方に対応した C 変換を単独で使用するには、2 つの基本型が明らかになっていなければなりません。さらに、キャスト式を追加するか、複数の書式設定文字列を定義する必要があります。D コンパイラでは、サイズ接頭辞の省略が可能で、引数のサイズが自動的に特定されるので、この問題は起こりません。
サイズ接頭辞は、フラグ、幅、精度の指定子と書式変換名の間 (書式変換名の直前) に挿入します。サイズ接頭辞には、以下のものがあります。
h (オプション)。d、i、o、u、x、X のいずれかの変換を short または unsigned short に適用します。
l (オプション)。d、i、o、u、x、X のいずれかの変換を long または unsigned long に適用します。
ll (オプション)。d、i、o、u、x、X のいずれかの変換を long long または unsigned long long に適用します。
L (オプション)。e、E、f、g、G のいずれかの変換を long double に適用します。
l (オプション)。変換 c を引数 wint_t に適用し、変換文字 s を wchar_t 引数のポインタに適用します。
各変換文字シーケンスでは、0 個以上の引数のフェッチが行われます。書式設定文字列の引数の指定が不適切な場合や、書式設定文字列がなくなって引数が余ってしまった場合、D コンパイラはエラーメッセージを発行します。未定義の変換書式が指定された場合も、D コンパイラはエラーメッセージを発行します。変換文字シーケンスは、以下のとおりです。
ポインタ (uintptr_t 引数) は、カーネルシンボル名 (module`symbol-name に任意の 16 進バイトオフセットを追加した形式) として出力されます。この値が、既知のカーネルシンボルで定義された範囲にない場合は、16 進整数として出力されます。
引数 char、short、int は ASCII 文字として出力されます。
引数 char、short、int は、プリント可能 ASCII 文字である場合、ASCII 文字として出力されます。出力可能文字でない場合、表 2–5 のように、対応するエスケープシーケンスを使って出力されます。
引数 char、short、int、long、long long は、10 進 (base 10) 整数として出力されます。引数が signed である場合、符号付きの値が出力されます。引数が unsigned である場合、符号なしの値が出力されます。この変換の効果は、i と同じです。
引数 float、double、long double は、[-]d. ddde±dd (基数文字の前は 1 桁、後ろは精度と同じ桁数) の形式に変換されます。引数がゼロ以外の場合、基数文字もゼロ以外になります。精度を指定しない場合は、デフォルトの精度値は 6 です。精度が 0 で # フラグを指定しない場合は、基数文字は表示されません。E 変換書式では、e ではなく E の付いた指数が生成されます。指数は必ず 2 桁以上になります。値は、適切な桁数で丸められます。
引数 float、double、long double は、[-]ddd.ddd (基数文字の後ろは精度と同じ桁数) の形式に変換されます。精度を指定しない場合は、デフォルトの精度値は 6 です。精度が 0 で # フラグを指定しない場合は、基数文字は表示されません。基数文字が表示されるときは、必ずその前に 1 桁以上の数字が表示されます。値は、適切な桁数で丸められます。
引数 float、double、long double は、f または e の形式、さらに G 変換文字の場合は E の形式で、出力されます。有効桁数を示す精度も出力されます。明示的な精度が 0 の場合、1 と見なされます。どの形式が使用されるかは、変換対象の値によって異なります。たとえば、e または E 形式は、変換の結果として得られた指数が、-4 より小さいか、または精度の値以上の場合にかぎって、使用されます。結果の小数部分がゼロの場合、この部分は省略されます。基数文字は、その後ろに桁がある場合にしか表示されません。# フラグを指定すると、結果の小数部分のゼロが省略されなくなります。
引数 char、short、int、long、long long は、10 進 (base 10) 整数として出力されます。引数が signed である場合、符号付きの値が出力されます。引数が unsigned である場合、符号なしの値が出力されます。この変換の効果は、d と同じです。
引数 char、short、int、long、long long は、符号なし 8 進 (base 8) 整数として出力されます。この変換では、signed の引数または unsigned の引数を使用できます。# フラグを指定すると、結果の最初の桁を強制的にゼロにする必要がある場合に、結果の精度が上がります。
ポインタ (uintptr_t) 引数は、16 進 (base 16) 整数として出力されます。D では、あらゆる型のポインタ引数を使用できます。# フラグを指定すると、結果がゼロ以外になった場合、その前に 0x が付加されます。
引数は、char の配列か string でなければなりません。配列または string のバイトが終端 NULL 文字またはデータの終わりまで読み取られ、解釈されたあと、ASCII 文字として出力されます。精度を省略した場合、精度は無限と見なされます。したがって、最初の NULL 文字までのすべての文字が出力されます。精度を指定した場合、文字配列のうち、対応するスクリーンカラム数で表示される部分だけが出力されます。書式設定の対象の引数が char * 型である場合、string にキャストするか、引数の前に D 演算子 stringof を付加します。この演算子を付加すると、DTrace は、文字列のバイト数だけをトレースし、書式設定を行うようになります。
引数は、char の配列か string でなければなりません。引数は、%s 変換の場合と同様にして処理されます。ただし、プリント可能な ASCII 文字でない場合は、表 2–5 のように、対応するエスケープシーケンスで置き換えられます。
引数 char、short、int、long、long long は、符号なし 10 進 (base 10) 整数として出力されます。この変換では、signed の引数または unsigned の引数を使用できます。結果は常に unsigned の書式になります。
引数 int はワイド文字 (wchar_t) に変換され、このワイド文字が出力されます。
引数は、wchar_t の配列でなければなりません。配列のバイトが終端 NULL 文字またはデータの終わりまで読み取られ、解釈されたあと、ワイド文字として出力されます。精度を省略した場合、精度は無限と見なされます。したがって、最初の NULL 文字までのすべてのワイド文字が出力されます。精度を指定した場合、ワイド文字配列のうち、対応するスクリーンカラム数で表示される部分だけが出力されます。
引数 char、short、int、long、long long は、符号なし 16 進 (base 16) 整数として出力されます。この変換では、signed の引数または unsigned の引数を使用できます。x 形式の変換の場合、文字数字 abcdef を使用します。X 形式の変換の場合、文字数字 ABCDEF を使用します。# フラグを指定すると、結果がゼロ以外になった場合、その前に 0x (%x の場合)、または 0X (%X の場合) が付加されます。
引数 uint64_t は、協定世界時の 1970 年 1 月 1 日 00:00 からの経過時間 (ナノ秒数) として解釈され、「%Y %a %b %e %T %Z」のような cftime(3C) 形式で出力されます。協定世界時の 1970 年 1 月 1 日 00:00 からの経過時間 (ナノ秒数) は、walltimestamp 変数で表されます。
パーセント記号 (%) を文字どおりに出力します。引数の変換は行われません。変換指定は %% だけです。
printa() 関数は、D プログラム内の集積体の結果の書式設定に使用します。この関数は、次のいずれかの形式で呼び出します。
printa(@aggregation-name); printa(format-string, @aggregation-name);
最初の形式を使用した場合、dtrace(1M) コマンドは、集積体 aggregation-name のデータのスナップショットを取り、集積体のデフォルト出力書式 (第 9 章集積体を参照) と同じ書式で結果を出力します。
2 番目の形式を使用した場合、dtrace(1M) コマンドは、集積体 aggregation-name のデータのスナップショットを取り、次の規則に従って format string に指定された変換を適用し、その結果を出力します。
集積体を作成する際に使用した組署名と一致する書式変換を使用する必要があります。組要素は、それぞれ 1 回ずつ使用できます。たとえば、次の D 文でカウントを集積するとします。
@a["hello", 123] = count(); @a["goodbye", 456] = count();
さらに、プローブ節に D 文 printa(format-string , @a) を追加すると、dtrace は集積体 aggregation-name のデータのスナップショットを取り、次の文を入力したときと同じ結果を出力します。
printf(format-string, "hello", 123); printf(format-string, "goodbye", 456);
集積体内に定義された各組についても同様です。
printf() の場合と異なり、printa() で使用する書式設定文字列には、組要素をすべて含める必要はありません。たとえば、長さ 3 の組と、書式変換 1 つだけを使用することも可能です。したがって、printa() 出力では、任意の組キーを省略できます。このためには、集積体の宣言に変更を加えて、省略したいキーを組の末尾に移動し、そのキーに対応する変換指定子を printa() 書式設定文字列から除外します。
出力に集積体の結果を含めるには、printa と併用する場合にかぎり有効な、書式設定フラグ文字 @() を追加します。@ フラグは、任意の適切な書式変換指定子と組み合わせて使用できます。また、単一の書式設定文字列内で複数回使用できます。このため、組の結果がどこに出力されるかは決まっておらず、場合によっては複数回出力されることもあります。集積関数と組み合わせて使用することができる変換指定子は、集積関数の結果の型によって決まります。集積体の結果の型は、次のとおりです。
avg() |
uint64_t |
count() |
uint64_t |
lquantize() |
int64_t |
max() |
uint64_t |
min() |
uint64_t |
quantize() |
int64_t |
sum() |
uint64_t |
たとえば、avg() の結果に書式を設定するには、%d、%i、%o、%u、%x のいずれかの書式変換を適用します。関数 quantize() と lquantize() は、結果を単一の値ではなく、ASCII テーブルとして書式設定します。
以下に、printa() を使用する D プログラムの例を示します。この例では、profile プロバイダを使って caller の値を収集し、結果を単純なテーブルとして書式設定しています。
profile:::profile-997 { @a[caller] = count(); } END { printa("%@8u %a\n", @a); }
dtrace でこのプログラムを実行し、しばらく待ってから Control-C キーを押します。すると、次のような出力が得られます。
# dtrace -s printa.d ^C CPU ID FUNCTION:NAME 1 2 :END 1 0x1 1 ohci`ohci_handle_root_hub_status_change+0x148 1 specfs`spec_write+0xe0 1 0xff14f950 1 genunix`cyclic_softint+0x588 1 0xfef2280c 1 genunix`getf+0xdc 1 ufs`ufs_icheck+0x50 1 genunix`infpollinfo+0x80 1 genunix`kmem_log_enter+0x1e8 ... |
printf() の代わりに trace() 関数を使ってデータを捕捉する場合、dtrace コマンドを実行すると、結果にデフォルトの出力書式が適用されます。データのサイズが 1、2、4、または 8 バイトである場合、結果は 10 進整数値として書式設定されます。これ以外のサイズで、バイトシーケンスとして解釈される出力可能文字列である場合、データは ASCII 文字列として出力されます。これ以外のサイズで、出力可能文字列でない場合、データは 16 進整数の書式が設定された連続するバイト値として出力されます。