[このドキュメントの単一ページバージョンも表示できます。]

Subversion のデバッグ

SVN_DBG によるデバッグ

SVN_DBG デバッグツールは、C プリプロセッサマクロであり、SVN テストスイートを妨げることなく、デバッグ出力を stdout(デフォルト)または stderr に送信します。

これは、gdb などのデバッガーの代替、またはデバッガーの使用を支援するための追加情報を提供します。特にデバッガーを使用できない状況で役立つ可能性があります。

svn_debug モジュールには、呼び出しのファイル:行と printf のような引数を `#SVN_DBG_OUTPUT` stdio ストリーム(デフォルトでは#stdout)に出力する 2 つのデバッグ支援マクロが含まれています。

SVN_DBG( ( const char *fmt, ...) ) /* double braces are neccessary */
SVN_DBG_PROPS( ( apr_hash_t *props, const char *header_fmt, ...) )

SVN_DBG 出力の制御

  • SVN_DBG は、svn が `--enable-maintainer-mode` で構成されている場合に常に有効になります。
  • SVN テストスイートは、SVN_DBG 出力を自動的にオフにします。出力を手動で抑制するには、シェル環境で `SVN_DBG_QUIET` 変数を 1 に設定します。
  • 完了したら、コミットするコードおよびリストに送信するパッチから、`SVN_DBG` および `SVN_DBG_PROPS` マクロのすべてのインスタンスを必ず削除してください。(別名:患者にメスを忘れないでください!)

SVN_DBG マクロの定義とコードは、以下にあります。

SVN_DBG マクロの使用法を示すサンプルパッチ

Index: subversion/libsvn_fs_fs/fs_fs.c
===================================================================
--- subversion/libsvn_fs_fs/fs_fs.c     (revision 1476635)
+++ subversion/libsvn_fs_fs/fs_fs.c     (working copy)
@@ -2303,6 +2303,9 @@ get_node_revision_body(node_revision_t **noderev_p
 
   /* First, try a cache lookup. If that succeeds, we are done here. */
   SVN_ERR(get_cached_node_revision_body(noderev_p, fs, id, &is_cached, pool));
+  SVN_DBG(("Getting %s from: %s\n", 
+           svn_fs_fs__id_unparse(id),
+           is_cached ? "cache" : "disk"));
   if (is_cached)
     return SVN_NO_ERROR;

SVN_DBG_PROPS マクロの使用法を示すサンプルパッチ

Index: subversion/svn/proplist-cmd.c
===================================================================
--- subversion/svn/proplist-cmd.c	(revision 1475745)
+++ subversion/svn/proplist-cmd.c	(working copy)
@@ -221,6 +221,7 @@ svn_cl__proplist(apr_getopt_t *os,
                                       URL, &(opt_state->start_revision),
                                       &rev, ctx, scratch_pool));
+      /*  this can be called with svn proplist  --revprop -r <rev> */
+      SVN_DBG_PROPS((proplist,"The variable apr_hash_t *proplist contains: "));
       if (opt_state->xml)
         {
           svn_stringbuf_t *sb = NULL;

サーバーのデバッグ

DAV サーバーのデバッグ

'mod_dav_svn.so' には、Subversion サーバーのメインロジックが含まれています。これは、httpd 内のモジュールとして実行される mod_dav 内のモジュールとして実行されます。httpd が動的共有モジュールを使用している場合は、次の操作が必要になる可能性があります。ブレークポイントを保留に設定します(~/.gdbinit 内)mod_dav_svn にブレークポイントを設定する前に、必要になります。または、httpd を起動し、中断し、ブレークポイントを設定して続行することもできます。

   % gdb httpd
   (gdb) run -X
   ^C
   (gdb) break some_func_in_mod_dav_svn
   (gdb) continue

-X スイッチは -DONE_PROCESS および -DNO_DETACH と同等であり、それぞれ httpd が単一スレッドとして実行され、tty に接続されたままになることを保証します。起動するとすぐに、リクエストを待機します。その時に control-C を押してブレークポイントを設定します。

Apache の実行時ログを監視するとよいでしょう

   /usr/local/apache2/logs/error_log
   /usr/local/apache2/logs/access_log

問題が発生している可能性とブレークポイントを設定する場所を特定するのに役立ちます。

または、./subversion/tests/cmdline/davautocheck.sh --gdb作業コピーで実行すると、その作業コピー内の mod_dav_svn を使用して httpd が起動します。次に、その上で個々の Python テストを実行できます。./basic_tests.py --url=http://localhost:3691/.

Unix 上の ra_svn クライアントとサーバーのデバッグ

ra_svn のバグは通常、次のいずれかの暗号化エラーメッセージで表れます。

  svn: Malformed network data
  svn: Connection closed unexpectedly

(最初のエラーメッセージは、トンネルモードでユーザードットファイルまたはフックスクリプトによってデータストリームが破損したことも意味します。 issue #1145 を参照してください。)最初のエラーメッセージは、通常、クライアントをデバッグする必要があることを意味します。2 番目のエラーメッセージは、通常、サーバーをデバッグする必要があることを意味します。

ra_svn をデバッグする最も簡単な方法は、--disable-shared --enable-maintainer-mode を使用してビルドすることです。後者のオプションを使用すると、エラーメッセージはブレークポイントを設定する正確な行を通知します。それ以外の場合は、"Malformed network data" エラーを返す marshal.c:vparse_tuple() の末尾にある行番号を調べます。

クライアントをデバッグするには、gdb で起動し、ブレークポイントを設定して、問題のあるコマンドを実行します。

  % gdb svn
  (gdb) break marshal.c:NNN
  (gdb) run ARGS
  Breakpoint 1, vparse_tuple (list=___, pool=___, fmt=___, 
    ap=___) at subversion/libsvn_ra_svn/marshal.c:NNN
  NNN                                 "Malformed network data");

いくつかの役立つ情報があります

  • バックトレースは、どのプロトコル交換が失敗しているかを正確に示します。

  • "print *conn" は、接続バッファを表示します。read_buf、read_ptr、および read_end は、読み取りバッファを表し、マーシャラーが参照しているデータを示すことができます。(read_buf は通常、read_end で 0 終端されていないため、バッファにガベージデータがあると誤って想定しないように注意してください。)

  • フォーマット文字列は、マーシャラーが何を期待しているかを決定します。

デーモンモードでサーバーをデバッグするには、gdb で起動し、ブレークポイントを設定します(通常、クライアントでの「接続が予期せず閉じられました」というエラーは、サーバーでの「不正なネットワークデータ」エラーを示しますが、コアダンプを示す場合もあります)。次に、"-X" オプションを指定して実行して、単一の接続を処理します。

  % gdb svnserve
  (gdb) break marshal.c:NNN
  (gdb) run -X

次に、問題のあるクライアントコマンドを実行します。そこからは、クライアントのデバッグと同じです。

トンネルモードでサーバーをデバッグするのは、より面倒です。svnserve の main() の先頭付近に "{ int x = 1; while (x); }" のようなものを追加し、結果の svnserve をサーバーのユーザーパスに配置する必要があります。次に、操作を開始し、サーバー上のプロセスに gdb をアタッチし、"set x = 0" を実行して、必要に応じてコードをステップスルーします。

ネットワークトラフィックのトレース

クライアントとサーバー間のネットワークトラフィックをトレースすると、デバッグに役立ちます。ネットワークトレースを実行する方法はいくつかあります(このリストは網羅的なものではありません)。

ネットワークトレースを実行する場合は、圧縮を無効にすることをお勧めします。— 以下のhttp-compressionのパラメーターを参照してください。servers構成ファイル。

Wireshark を使用したネットワークトレース

Wireshark(以前は「Ethereal」として知られていました)を使用して、会話を盗聴します。

まず、同じ Wireshark セッション内のキャプチャ間で「クリア」を押していることを確認してください。そうしないと、あるキャプチャ(たとえば、HTTP キャプチャ)からのフィルターが他のキャプチャ(たとえば、ra_svn キャプチャ)を妨げる可能性があります。

クリアしたと仮定すると、

  1. キャプチャ メニューを引き下げ、キャプチャフィルター を選択します。

  2. http://(WebDAV)プロトコルをデバッグしている場合は、表示されるウィンドウで "HTTP TCP ポート (80)" を選択します(これにより、フィルタ文字列 "tcp ポート http" になります)。

    svn://(ra_svn)プロトコルをデバッグする場合は、新規 を選択し、新しいフィルターに名前(「ra_svn」など)を付け、フィルター文字列ボックスに "tcp ポート 3690" と入力します。

    完了したら、「OK」をクリックします。

  3. もう一度 キャプチャ メニューに移動し、今回は インターフェイス を選択して、適切なインターフェイス(おそらく、クライアントと同じマシンでサーバーが実行されていると仮定して、「ループバック」のインターフェイス「lo」が必要でしょう)の横にある オプション をクリックします。

  4. 該当するチェックボックスをオフにして、無差別モードをオフにします。

  5. 右下の 開始 ボタンをクリックして、キャプチャを開始します。

  6. Subversion クライアントを実行します。

  7. 操作が完了したら、停止アイコン(イーサネットインターフェイスカード上の赤い X)をクリックします(または、キャプチャ->停止 が機能するはずです)。これで、キャプチャが完了しました。これは、巨大な行のリストのように見えます。

  8. プロトコル 列をクリックして並べ替えます。

  9. 次に、最初の関連行をクリックして選択します。通常、これは最初の行です。

  10. 右クリックし、TCP ストリームを追跡 を選択します。Subversion クライアントの HTTP 変換のリクエスト/レスポンスのペアが表示されます。

上記の説明は、Wireshark(バージョン 0.99.6)のグラフィカルバージョンに固有であり、コマンドラインバージョンの「tshark」(Wireshark が Ethereal と呼ばれていた頃の「tethereal」に対応)には適用されません。

socat を使用したネットワークトレース

別の方法は、Subversion クライアントとサーバーの間にロギングプロキシを設定することです。これを行う簡単な方法は、socat プログラムを使用することです。たとえば、svnserve インスタンスとの通信をログに記録するには、次のコマンドを実行します。

socat -v TCP-LISTEN:9630,reuseaddr,fork TCP4:localhost:svn

次に、次の URL ベースを使用して svn コマンドを実行します。svn://127.0.0.1:9630/; socatは、ポート 9630 から通常の svnserve ポート(3690)にトラフィックを転送し、両方向のすべてのトラフィックを標準エラーに出力し、トラフィックの方向を示すために < および > 記号をプレフィックスとして付加します。

暗号化された https:// 接続から読み取り可能な HTTP をログに記録するには、TLS を使用してサーバーに接続する socat プロキシを実行します。

socat -v TCP-LISTEN:9630,reuseaddr,fork OPENSSL:example.com:443

次に、それをプレーンな http:// 接続のプロキシとして使用します。

svn ls http://example.com/svn/repos/trunk \
    --config-option servers:global:http-proxy-host=localhost \
    --config-option servers:global:http-proxy-port=9630 \
    --config-option servers:global:http-compression=no

socat プロキシはプレーンな HTTP をログに記録し、サーバーとのすべてのネットワークトラフィックは TLS を使用して暗号化されます。

http-proxy を使用したネットワークトレース

http クライアント/サーバー設定をデバッグしている場合は、CharlesFiddler などの Web デバッグプロキシを使用できます。

このようなプロキシを設定したら、Subversion がプロキシを使用するように構成する必要があります。これは、以下のhttp-proxy-hosthttp-proxy-portのパラメーターを使用して行うことができます。servers設定ファイルを使用します。また、以下のオプションを使用してコマンドラインでプロキシを指定することもできます。--config-option 'servers:global:http-proxy-host=127.0.0.1' --config-option 'servers:global:http-proxy-port=8888'.

メモリリークの追跡

APRプールを使用しているため、厳密な意味でのメモリリークは通常発生しません。割り当てたメモリはすべて最終的にはクリーンアップされます。しかし、場合によっては、操作が本来よりも多くのメモリを使用することがあります。たとえば、大規模なソースツリーのチェックアウトは、小規模なソースツリーのチェックアウトよりも多くのメモリを使用すべきではありません。このような場合、通常は、寿命が長すぎるプールからメモリを割り当てていることを意味します。

お気に入りのメモリリーク追跡ツールがある場合は、--enable-pool-debug (これにより、すべてのプール割り当てで独自のmalloc()が使用されるようになります) で設定し、操作の途中で終了するように設定して、そのツールを使用できます。そうでない場合は、別の方法を以下に示します。

  • --enable-pool-debug=verbose-alloc で設定します。すべての割り当てがファイルと行の情報を受け取るように、APRとSubversionをすべて再構築してください。

  • 操作を実行し、stderrをファイルにパイプします。十分なディスク容量があることを願います。

  • ファイルには、次のような行が多数表示されます。

        POOL DEBUG: [5383/1024] PCALLOC (      2763/      2763/      5419) \
        0x08102D48 "subversion/svn/main.c:612"                             \
        <subversion/libsvn_subr/auth.c:122> (118/118/0)
       

    最も重要なのは10番目のフィールド(引用符で囲まれたフィールド)で、この割り当てのプールが作成されたファイルと行番号を示しています。そのファイルと行番号に移動して、プールの寿命を特定します。上記の例では、main.c:612 は、この割り当てが svn クライアントのトップレベルプールで行われたことを示しています。この割り当てが操作中に何度も繰り返される場合は、メモリリークの原因を示している可能性があります。11番目のフィールド(角括弧で囲まれたフィールド)は、割り当て自体のファイルと行番号を示しています。