動的トレーシングは、複雑なオンラインソフトウェアシステムの詳細を深く調査するための重要なツールです。これにより、ソフトウェア自体を変更することなく、これらのシステムにおける様々な問題の根本原因を正確に特定することができます。OpenResty XRay では、C 互換の Y 言語(Ylang)を含む高度なツールセットを提供しており、標準 C コードを使用して対象の C プログラムを効果的にトレースすることができます。Y language ツールキットを ylang 最適化コンパイラでコンパイルすると、eBPF+1、Stap+ 2、または GDB と互換性のある 動的トレーシング アナライザーを生成できます。しかし、HotSpot JVM や NodeJS などの広く使用されているオープンソースソフトウェアを含む、現実世界で非常に一般的な C++ アプリケーションを扱う際には、一定のギャップが存在していました。

このギャップを埋めるために、私たちは Y++ という名前のカスタム C++ コンパイラを開発しました。これは Y language コードまたは GNU C のスーパーセットを生成することを目的としています。注目すべきは、Y++ がすでに C++23 言語標準のほぼ全体をサポートしていることです。本記事では、Y++ の機能のいくつかを非常に簡単な例を用いて紹介します。

対象 C++ プログラムを設定

トレース対象として、シンプルながら完全な C++ プログラムを使用します。このプログラムは、グローバル変数 int_vec で STL コンテナ std::vector を利用しています。

/* target.cxx:追跡用のサンプル C++ プログラム */

#include <vector>
#include <cstdio>

std::vector<int> int_vec;

int main(void) {
    int sum = 0;
    std::vector<int>::iterator it;

    for (int i = 1; i <= 3; i++) {
        int_vec.push_back(i * 2);
    }
    for (it = int_vec.begin(); it != int_vec.end(); ++it) {
        sum += *it;
    }
    return sum - 12;
}

このプログラムは、標準的な C++ コンパイラコマンドでコンパイルできます:

g++ -g -O2 target.cxx -o target

必要であれば、-fPIC -pieのようなオプションを含めることもできます。

実行対象プログラムを実行し、期待される機能を確認してください:

$ ./target; echo $?
0

次に、このプロセスを動的にトレースするための外部 C++ コードスニペットを作成します。

C++(または Y++)アナライザーの作成

eBPF+ または Stap+ ツールチェーンを利用して 動的トレーシング アナライザーを開発します。このアナライザーは、グローバル C++ 変数 int_vec を監視し、そのすべての要素をリアルタイムで出力します。以下は、それを実現するための analyzer.yxx ツールです。(.yxx ファイル拡張子は Y++ ソースファイルを示します。)

/* analyzer.yxx:C++ 用動的トレースツール */

#include <vector>
#include <cstdio>

_target std::vector<int> int_vec;

static void probe_handler(void) {
    for (auto it = int_vec.begin(); it != int_vec.end(); ++it) {
        printf("%d\n", *it);
    }
}

_probe main() -> int {
    probe_handler();
}

このコードスニペットは、標準 C++ 言語の拡張機能をいくつか利用しています。_target キーワードは対象プログラム内のシンボルを指定し、ここでは int_vec 変数を対象アプリケーション内で見つかる変数として宣言しています。_probe キーワードは動的プローブ位置を宣言し、Y 言語 の構文構造に似ており、対象プログラムの main 関数が戻る時点でのプローブ位置を定義しています。トレーサーコード内で vectorcstdio などの標準ヘッダーファイルをインクルードしていることに注意してください。これにより、トレーサーは対象プログラム内の int_vec の値を出力できるようになります。

y++ コンパイラを使用して analyzer.yxx ファイルを Y language コードにコンパイルします:

y++ -o analyzer.y analyzer.yxx

その後、Y language ツールチェーンを使用して、このアナライザーを eBPF+、Stap+、または GDB など、様々なプラットフォームで実行可能なツールにコンパイルできます。以下は eBPF+ を使用した例です:

ylang --gen-bpf --symtab debuginfo.jl analyzer.y

debuginfo.jl ファイルは、target バイナリファイルの DWARF デバッグシンボルから自動的に生成されます。このプロセスには -g コンパイルオプションが必要です。この要件を緩和する方法については、後ほど説明します。

ylang コマンドは、eBPF+ ツールチェーン用に 2 つのファイルを生成します:カーネル空間用の analyzer.bpf.c とユーザー空間用の analyzer.ubpf.c です。

これらの生成された C 言語ソースファイルは、clanggcc ツールチェーンを使用して、それぞれ eBPF バイトコードと実行可能ファイル analyzer にコンパイルできます。analyzer は eBPF バイトコードをロードするために使用されます(clang コンパイラの eBPF バックエンドも強化しています)。

対象プログラムとアナライザーの実行

上記の手順で生成された analyzer 実行可能ファイルを使用して、対象プログラムを起動し、分析用の eBPF プログラムをロードすることができます。例は以下の通りです:

$ ./analyzer ~/work/target
Start tracing...
2
4
6

この出力は、アナライザーが対象プログラム内のグローバル変数 int_vec の値を動的にトレースし、出力する能力を確認しています。実行中の対象プロセスに対しては、アナライザーは PID を使用して特定のトレースをサポートしています:

./analyzer -p 12345 -t 3

このコマンドは、指定された PID のプロセスをトレースし、最大 3 秒間分析します。

複雑に見えるかもしれませんが、OpenResty XRay 製品は、このワークフロー全体を自動化することができます。

複雑な C++ アプリケーションのサポート強化

現在、クラス継承、仮想関数テーブル、new および delete 演算子を使用した動的メモリ割り当てなど、多くの C++ 機能をサポートしています。

また、HotSpot JVM、NodeJS、または MySQL 内の C++ コードスニペットなど、より複雑なトレーサー C++ プログラムのサポート範囲を拡大する努力も続けています。これにより、JVM、Java プログラム、およびより広範な C++ アプリケーションに適用できる、より多くの非侵襲的な 動的トレーシング アナライザーを作成できるようになります。この分野で急速に進展を遂げており、今後のアップデートにご期待ください。

デバッグシンボルについて

デバッグシンボルは、バイナリ実行可能ファイルのトレース(またはデバッグ)に不可欠であり、g++ コマンドでコンパイルする際に -g オプションを使用する必要があります。この要件は、アプリケーションの実行速度を低下させる可能性があるデバッグビルドとは異なります。重要なのは、デバッグシンボルは実行時のオーバーヘッドを生じさせず、プロセス実行中にメモリにロードされないことです。

現実世界のシナリオでは、デバッグシンボルが失われていたり、対象プログラムが -g オプションでコンパイルされていない場合があります。OpenResty XRay の独自の AI および機械学習アルゴリズムは、ストリップされた ELF バイナリファイルからデバッグシンボルを再生成することができます(内部の symgen ツールを通じて)。これは、Nginx、LuaJIT、Python などの一般的なオープンソースソフトウェアや他の多くのソフトウェアに適用可能です。また、クライアントの特定のプログラムに対してカスタムモデルトレーニングを提供し、事実上あらゆるプロセスに 動的トレーシング の機能を拡張しています。

詳しくは当社の YouTube のビデオをご覧ください。

結論

私たちが独自に開発した C++ コンパイラ Y++ は、動的トレーシング の分野における重要な進歩を示しています。動的トレーシング の能力を C++ アプリケーションに拡張することで、重要なギャップを埋めただけでなく、より複雑な分析と可観測性技術への道を開きました。本記事で提供された例は、Y++ と私たちの 動的トレーシング(eBPF+ と Stap+ を含む)のすべての可能性の氷山の一角に過ぎません。

ツールを継続的に改良し、その機能を拡張していくにつれて、開発者やシステム管理者がアプリケーションをより深く理解し、より信頼性が高く、パフォーマンスの優れたソフトウェアを作成できる素晴らしい未来を期待しています。大規模な分散システムで複雑な問題を解決しようとしているか、ソフトウェアのパフォーマンスを最適化しようとしているかに関わらず、本記事で議論されたツールと方法は、侵襲的なコード変更を行うことなく、目標を達成するための堅固なフレームワークを提供します。

さらに、HotSpot JVM 内のアプリケーションを含む、より複雑な C++ アプリケーションのサポートに向けた継続的な取り組みは、イノベーションとより広範なソフトウェア開発コミュニティへのコミットメントを示しています。動的トレーシングの力を最大限に活用し、OpenResty XRay のような私たちの独自技術と組み合わせることで、今日の開発者が直面する実際の課題に対するソリューションを策定する最前線に立っています。

OpenResty XRay について

OpenResty XRay動的トレーシング製品であり、実行中のアプリケーションを自動的に分析して、パフォーマンスの問題、動作の問題、セキュリティの脆弱性を解決し、実行可能な提案を提供いたします。基盤となる実装において、OpenResty XRay は弊社の Y 言語によって駆動され、Stap+、eBPF+、GDB、ODB など、様々な環境下で複数の異なるランタイムをサポートしております。

著者について

章亦春(Zhang Yichun)は、オープンソースの OpenResty® プロジェクトの創始者であり、OpenResty Inc. の CEO および創業者です。

章亦春(GitHub ID: agentzh)は中国江蘇省生まれで、現在は米国ベイエリアに在住しております。彼は中国における初期のオープンソース技術と文化の提唱者およびリーダーの一人であり、Cloudflare、Yahoo!、Alibaba など、国際的に有名なハイテク企業に勤務した経験があります。「エッジコンピューティング」、「動的トレーシング」、「機械プログラミング」 の先駆者であり、22 年以上のプログラミング経験と 16 年以上のオープンソース経験を持っております。世界中で 4000 万以上のドメイン名を持つユーザーを抱えるオープンソースプロジェクトのリーダーとして、彼は OpenResty® オープンソースプロジェクトをベースに、米国シリコンバレーの中心部にハイテク企業 OpenResty Inc. を設立いたしました。同社の主力製品である OpenResty XRay動的トレーシング技術を利用した非侵襲的な障害分析および排除ツール)と OpenResty XRay(マイクロサービスおよび分散トラフィックに最適化された多機能ゲートウェイソフトウェア)は、世界中の多くの上場企業および大企業から高い評価を得ております。OpenResty 以外にも、章亦春は Linux カーネル、Nginx、LuaJITGDBSystemTapLLVM、Perl など、複数のオープンソースプロジェクトに累計 100 万行以上のコードを寄与し、60 以上のオープンソースソフトウェアライブラリを執筆しております。

翻訳

英語版の原文と日本語訳版(本文)をご用意しております。読者の皆様による他の言語への翻訳版も歓迎いたします。全文翻訳で省略がなければ、採用を検討させていただきます。心より感謝申し上げます!


  1. eBPF+ は、既存の Linux カーネルにパッチを当てることなく実行できる、私たちが強化した eBPF バージョンです。 ↩︎

  2. Stap+ は、拡張機能を提供する SystemTap の高度なバージョンです。 ↩︎