DTracepidプロバイダーを使用したnginxのデバッグ
DTracepidプロバイダーを使用したnginxのデバッグ
この記事は、読者がnginx内部とDTraceの一般的な知識を持っていることを前提としています。
-with-debug オプションで構築されたnginxは、リクエスト処理に関する多くの情報をすでに提供していますが、コードパスの特定の部分をより完全にトレースし、同時に残りのデバッグを省略することが望ましい場合があります出力。 DTrace pidプロバイダー(Solaris、macOSで利用可能)は、コードの変更を必要とせず、タスクを支援できるため、ユーザーランドプログラムの内部を探索するための便利なツールです。 nginx関数呼び出しをトレースして印刷するための単純なDTraceスクリプトは、次のようになります。
#pragma D option flowindent pid$target:nginx::entry { } pid$target:nginx::return { }
ただし、関数呼び出しトレースのDTrace機能は、限られた量の有用な情報しか提供しません。 関数の引数のリアルタイム検査は、通常、より興味深いものですが、もう少し複雑でもあります。 以下の例は、読者がDTraceとDTraceを使用してnginxの動作を分析するプロセスをよりよく理解できるようにすることを目的としています。
nginxでDTraceを使用するための一般的なシナリオの1つは、次のとおりです。nginxワーカープロセスに接続して、リクエスト行とリクエスト開始時刻をログに記録します。 アタッチする対応する関数はngx_http_process_request()
であり、問題の引数はngx_http_request_t
構造体へのポインターです。 このようなリクエストロギング用のDTraceスクリプトは、次のように単純にすることができます。
pid$target::*ngx_http_process_request:entry { this->request = (ngx_http_request_t *)copyin(arg0, sizeof(ngx_http_request_t)); this->request_line = stringof(copyin((uintptr_t)this->request->request_line.data, this->request->request_line.len)); printf("request line = %s\n", this->request_line); printf("request start sec = %d\n", this->request->start_sec); }
上記の例では、DTraceにはngx_http_request_t
構造に関する知識が必要であることに注意してください。 残念ながら、DTraceスクリプトで特定の#include
ディレクティブを使用して、それをCプリプロセッサ(-C
フラグ付き)に渡すことは可能ですが、実際には機能しません。 相互依存関係が多いため、ほとんどすべてのnginxヘッダーファイルをインクルードする必要があります。 次に、configure
スクリプト設定に基づいて、nginxヘッダーにはPCRE、OpenSSL、およびさまざまなシステムヘッダーファイルが含まれます。 理論的には、特定のnginxビルドに関連するすべてのヘッダーファイルがDTraceスクリプトの前処理とコンパイルに含まれている可能性がありますが、実際には、一部のヘッダーファイルの構文が不明なため、DTraceスクリプトはコンパイルに失敗する可能性があります。
上記の問題は、DTraceスクリプトに関連する必要な構造とタイプの定義のみを含めることで解決できます。 DTraceは、構造、タイプ、およびフィールドオフセットのサイズを認識している必要があります。 したがって、DTraceで使用するために構造体定義を手動で最適化することにより、依存関係をさらに減らすことができます。
上記のDTraceスクリプトの例を使用して、正しく機能するために必要な構造体の定義を見てみましょう。
まず、configureによって生成されたobjs/ngx_auto_config.h
ファイルを含める必要があります。これは、さまざまな#ifdef
に影響を与える定数の数を定義しているためです。 その後、ngx_str_t
、ngx_table_elt_t
、ngx_uint_t
などのいくつかの基本的なタイプと定義。 DTraceスクリプトの先頭に配置する必要があります。 これらの定義はコンパクトで、一般的に使用されており、頻繁に変更されることはほとんどありません。
次に、他の構造体への多くのポインタを含むngx_http_request_t
構造体があります。 これらのポインターはこのスクリプトとは実際には無関係であり、サイズも同じであるため、voidポインターに置き換えることができます。 ただし、定義を変更する代わりに、適切なtypedefを追加することをお勧めします。
typedef ngx_http_upstream_t void; typedef ngx_http_request_body_t void;
最後になりましたが、2つのメンバー構造の定義(ngx_http_headers_in_t
、ngx_http_headers_out_t
)、コールバック関数の宣言、および定数の定義を追加する必要があります。
最終的なDTraceスクリプトは、ここからダウンロードできます。
次の例は、このスクリプトを実行した場合の出力を示しています。
# dtrace -C -I ./objs -s trace_process_request.d -p 4848 dtrace: script 'trace_process_request.d' matched 1 probe CPU ID FUNCTION:NAME 1 4 .XAbmO.ngx_http_process_request:entry request line = GET / HTTP/1.1 request start sec = 1349162898 0 4 .XAbmO.ngx_http_process_request:entry request line = GET /en/docs/nginx_dtrace_pid_provider.html HTTP/1.1 request start sec = 1349162899
同様の手法を使用して、リーダーは他のnginx関数呼び出しをトレースできるはずです。