DTrace の使用は、システム内での作業量の増加につながります。そのため、DTrace を有効にすると、必ずシステムパフォーマンスに何らかの影響があります。通常、この影響はごくわずかです。しかし、処理コストの高い有効化によって多くのプローブを有効化した場合は、無視できない影響を与える可能性があります。この章では、DTrace のパフォーマンスへの影響を最小限に抑えるテクニックを紹介します。
動的計測機能の技術により、DTrace では、カーネルや任意のユーザープロセスについて、広範囲のトレース対象が提供されています。この対象範囲の広さによって、システムの動作を調べる能力は飛躍的に向上しました。しかし、一方では、プローブの影響が非常に大きくなったと言うこともできます。何万個というレベルで大量のプローブを有効にした場合、システムに多大な影響が出るのは明らかです。したがって、問題解決に必要最低限のプローブだけを有効にしてください。たとえば、より小規模な有効化で問題が解決できるならば、すべての FBT プローブを有効にすることは避けてください。特定のモジュールや、特定の関数に注目するだけで、必要な答えが得られる可能性があります。
pid プロバイダを使用するときは、特に注意が必要です。pid プロバイダは、すべての命令を計測する可能性があります。その場合、アプリケーション内の大量のプローブが有効になり、ターゲットプロセスの処理速度が異常に遅くなってしまいます。
DTrace は、大量のプローブを有効にしなければ問題への答えが得られないような状況でも使用できます。大量のプローブを有効にすると、システムの処理速度が低減しますが、そのせいで致命的な障害が発生するわけではありません。必要な場合は、大量のプローブを有効にしても、まったく問題はありません。
第 9 章集積体で説明したように、DTrace の集積体を使用すると、データをスケーラブルに集積できます。連想配列にも、集積体とよく似た働きがあります。しかし、大域的な汎用変数としての性質上、集積体のようなリニアなスケーラビリティを提供できません。可能なかぎり、連想配列ではなく集積体を使用してください。以下は、推奨しない例です。
syscall:::entry { totals[execname]++; } syscall::rexit:entry { printf("%40s %d\n", execname, totals[execname]); totals[execname] = 0; }
以下が、望ましい例です。
syscall:::entry { @totals[execname] = count(); } END { printa("%40s %@d\n", @totals); }
DTrace では、述語を使って、計測結果から不要なデータを排除できます。指定された条件が満たされた場合だけ、データのトレースが行われます。多数のプローブを有効にするときは、通常、特定のスレッド (複数可) を識別するような形式の述語を使用します。該当する例として、/self->traceme/ や /pid == 12345/ を挙げることができます。これらの述語の多くは、ほとんどのプローブのほとんどのスレッドに対して偽を返します。しかし、プローブの数があまりにも多いと、この評価自体にかなりのコストがかかるようになります。このコストを下げるため、DTrace は、スレッド固有変数 (/self->traceme/ など) や不変変数 (/pid == 12345/ など) だけが含まれている述語の評価をキャッシュに格納します。キャッシュに入った述語は、キャッシュに入っていない述語よりもずっと低コストで評価できます。特に、その述語に、スレッド固有変数や文字列比較などの比較的コストの高い操作が含まれている場合、この傾向は顕著になります。通常、述語のキャッシュをユーザーが意識することはありませんが、ここからは、より良い述語を作成するための指針を得ることができます。次の表を参照してください。
キャッシュ可能 |
キャッシュ不能 |
---|---|
self->mumble |
mumble[curthread]、mumble[pid, tid] |
execname |
curpsinfo->pr_fname、curthread->t_procp->p_user.u_comm |
pid |
curpsinfo->pr_pid、 curthread->t_procp->p_pipd->pid_id |
tid |
curlwpsinfo->pr_lwpid、 curthread->t_tid |
curthread |
curthread->any member 、curlwpsinfo->any member、curpsinfo->any member |
以下は、推奨しない例です。
syscall::read:entry { follow[pid, tid] = 1; } fbt::: /follow[pid, tid]/ {} syscall::read:return /follow[pid, tid]/ { follow[pid, tid] = 0; }
次のように、スレッド固有変数を使用する方法を推奨します。
syscall::read:entry { self->follow = 1; } fbt::: /self->follow/ {} syscall::read:return /self->follow/ { self->follow = 0; }
キャッシュ可能な式だけを含む述語のみが、キャッシュ可能です。以下の述語は、すべてキャッシュ可能です。
/execname == "myprogram"/ /execname == $$1/ /pid == 12345/ /pid == $1/ /self->traceme == 1/
以下の述語では、大域変数が使用されています。そのため、これらはすべてキャッシュ不能です。
/execname == one_to_watch/ /traceme[execname]/ /pid == pid_i_care_about/ /self->traceme == my_global/