プログラミング
インライン化された関数のデバッグ情報
Debugging Information for Inlined Functions (lwn.net)
要約
BPFプログラムは、カーネル内の関数と連携するためにBTF(BPF Type Format)デバッグ情報を使用しますが、インライン化された関数は単一のアドレスを持たないため、現在トレースできません。LSFMM+BPFカンファレンスでは、インライン化された関数に関する情報をBTFに追加し、それらをトレース可能にするための提案が議論されました。この変更により、カーネルのデバッグ能力が向上し、インライン化された関数の呼び出しをより正確に追跡できるようになります。
全文翻訳
BPFプログラムは、カーネル内の関数と連携するためにBPF型フォーマット(BTF)デバッグ情報を使用します。具体的には、カーネル関数をトレースするには、カーネルのBTFセクションでそのアドレスを見つける必要がありますが、これはインライン化された関数、つまり単一の特定のアドレスを持たない関数には機能しません。Alan Maguire氏は、インライン化された関数をトレースできるようにするためにBTFにそれらに関する情報を追加したいと考えており、2026年Linuxストレージ、ファイルシステム、メモリ管理、BPFサミットでそのトピックに関するセッションを主導しました。
Maguire氏によると、カーネルには10万以上のインライン化された関数があり、それらは5倍以上の場所に分散しています。さらに悪いことに、一部の関数は部分的にインライン化されており、場所によっては通常通り呼び出され、別の場所ではインライン化されています。これにより、今日では関数が正常にトレースされたように見えても、一部の呼び出しが見られなかったというケースが発生する可能性があります。
良いニュースは、インライン化された関数をトレースするための残りのインフラストラクチャは、任意の場所にアタッチできるkprobeを有効にするためにすでに整っていることです。Maguire氏は、インライン化された関数のインライン化場所に関するデータを、使用可能な形式で取得するだけだと述べています。「ストーリーは実際にはかなり完成しています。」
では、この情報をBTFに格納するには何が必要でしょうか?DWARFデバッグフォーマットにはすでにインライン化情報を示す方法がありますが、DWARFも扱いにくく、一般的なケースを単純に表現する方法がありません。Maguire氏によると、BTFのソリューションはコンパクトで、重複排除を可能にし、メモリオーバーヘッドを低く保つ必要があります。理想的には、インライン化情報はカーネルバイナリの別のセクションに格納されるか、必要になるまでロードされないように、別のカーネルモジュールとして配布される可能性があります。
具体的には、Maguire氏はBTFに3つの新しい情報が追加されることを提案しました。最初のものは、各呼び出しサイトでどの関数がインライン化されたか、そしてインライン化されなかった場合にどのように呼び出されたかを示すインラインサイト固有の情報で、「ロケーションセクション」と呼ばれます。このデータは、特定の呼び出しサイトに固有であるため、簡単に重複排除できません。そのため、可能な限り、情報は重複排除可能なデータへのポインタで構成されるべきです。
彼が追加したい2番目と3番目の情報は、参照されるデータ、すなわち「ロケーションプロトタイプ」と「ロケーションパラメータ」です。ロケーションプロトタイプは、インライン化された関数の引数が呼び出しサイトでどのように表現されるかを、ロケーションパラメータへのポインタのリストとして指定します。各ロケーションパラメータは、単一の関数引数にアクセスする方法を格納します。理論的には、コンパイラは、インライン化された各呼び出しサイト(538,090件存在)に対して、関数パラメータを異なる場所に格納できます。実際には、コンパイラが変換するパラメータの方法は限られており、多くの関数は互換性のあるシグネチャを持っているため、コンパイラは同じ選択を行います。現在のカーネルでは、ロケーションプロトタイプを重複排除すると、わずか57,141個の異なるエントリになり、合計で17,535個のロケーションパラメータエントリしか参照しません。これは、ロケーションセクションが追加データの大部分を占めることを意味します。
全体として、Maguire氏が提案するBTFへの追加は、約11MBの追加データ、またはインライン化された呼び出しサイトあたり約21バイトになります。これを別のカーネルモジュールに抽出し、圧縮すると、総データ量は3.5MBに減少します。
Maguire氏は、特定の関数に関する情報がどのように格納されるかの例を説明しました。この関数を考えてみましょう:int foo(int a, void *b, bool c); コンパイラが、aを未使用として削除し、bをレジスタで渡すように昇格させ、cが定数であると判断する方法でfoo()をインライン化することを選択した場合、BTF表現は、foo()のBTF型ID、メモリ内のカーネルのベースアドレスからの呼び出しサイトのオフセット、およびロケーションプロトタイプエントリへのポインタを格納する単一のロケーションセクションエントリになります。そのエントリは、ロケーションパラメータエントリへの参照の長さタグ付き配列になります。最初のエントリはnullで、aが回復できないことを示します。2番目のエントリは、値がレジスタに含まれていることを示すフラグ、およびbの特定のレジスタ番号を持つロケーションパラメータエントリを参照します。最後のロケーションパラメータエントリは、定数であることを指定する別のフラグを持ち、その後に定数の値が続きます。
Alexei Starovoitov氏は、カーネルがトレースを設定する際に適切なエントリを見つけるために検索する必要があるため、ロケーションセクションのエントリはどのような順序で格納されるかを尋ねました。最初は、Maguire氏は呼び出しサイトのアドレスでソートするのが最善だと考えましたが、データの使用方法を見た後、代わりにエントリを関数名でソートすることにしました。これにより、特定の関数の情報を探しているトレーサーは、バイナリ検索で迅速に見つけることができます。
Maguire氏がカバーした、BTFにインライン化情報を追加する際の2つの前提問題がありました。まず、ロケーションセクションのエントリ数が、コンテナ構造の長さフィールドを24ビットに増やす必要がありました。より深刻なのは、BTFを処理する既存のツールのいくつかが、読み方がわからない新しいタグの存在にうまく対応できなかったことです。これは、BTFが新しい種類の情報で拡張されるたびに発生する問題であり、DWARFが非常に壊れやすくなった理由の一部です。これに対処するために、Maguire氏はBTFを解析する方法に関する情報をBTF自体に追加しました。これで、そのメタ情報を読み取ることができるツールは、理解できない新しいタグを正しくスキップできます。
Maguire氏はまた、新しい種類のBTFタグを適切にソートするために、poke-a-hole(pahole)ユーティリティを更新する必要がありました。これは少し複雑になりましたが、通常のカーネルビルドでは機能するはずです。
Andrii Nakryiko氏は、ツリー外モジュールをどのように処理するかについて尋ねました。Maguire氏は、それらのモジュールには、実行中のカーネルにロードされたときに明示的に再配置できる、回復力のあるモジュールBTFが必要になると説明しました。彼の設計はそれを可能にしますが、ビルドはいくらか複雑になります。ツリー外モジュールをエレガントにサポートするために必要な変更について多少のやり取りがありましたが、最終的に合意された変更はありませんでした。
執筆時点では、Maguire氏のパッチセットはマージされていません。インライン化された関数や部分的にインライン化された関数をトレースできることは明らかに有用ですが、問題は、それが複雑さとメモリオーバーヘッドに見合うかどうかです。時が経てばわかるでしょう。
この記事のインデックスエントリ
Kernel
BPF Conference
Storage, Filesystem, Memory-Management and BPF Summit/2026
コメントを投稿するには
この症候群には名前があるはずだ
2026年7月29日 19:59 UTC (水) roc (subscriber, #30627) [Link] (4件の返信)
> これは、BTFが新しい種類の情報で拡張されるたびに発生する問題であり、DWARFが非常に壊れやすくなった理由の一部です。
> 既存のソリューションを見て、自分のニーズには過剰だと判断し、自分が気にかけているユースケースだけに対処する新しいものを実装し、時間の経過とともにそのユースケースのセットが成長し、元のソリューションを却下した理由の多くを再導入する問題まで、ソリューションを段階的に拡張していくというケースのように感じます。そして、彼らは、元のソリューションよりもある点では優れているが、他の点では劣っている独自の代替ソリューションのポイントに到達します。そして、ツールを書く人やエコシステムを扱う人は皆、両方のソリューションを理解し、処理し、おそらくそれらの間で変換する必要があります。参照:BPF。
この症候群には名前があるはずだ
2026年7月29日 20:14 UTC (水) daroc (editor, #160859) [Link] (1件の返信)
> 必ずしも同意しないわけではありません。しかし、それは実際にはオープンソースエコシステム全体にとって伝統的なものです。つまり、これはすべてLinuxカーネルの文脈で行われており、それもその軌跡をたどりました。既存のシステムに対する小さな、自分のためのスクラッチ機能として始まり、その後それをはるかに超えて成長しました。