[您也可以檢視 單頁版本 的文件。]

除錯 Subversion

使用 SVN_DBG 除錯

SVN_DBG 除錯工具是一個 C 預處理器巨集,用於將除錯輸出傳送至 stdout(預設)或 stderr,同時不會干擾 SVN 測試套件。

它提供了一個替代方案,例如 gdb 等除錯器,或者提供額外的資訊來協助使用除錯器。它在無法使用除錯器的情況下特別有用。

svn_debug 模組包含兩個除錯輔助巨集,用於列印呼叫的檔案:行,以及類似 printf 的引數至 #SVN_DBG_OUTPUT stdio 串流(預設為 #stdout)

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

控制 SVN_DBG 輸出

  • 只要使用 --enable-maintainer-mode 設定 svn,就會啟用 SVN_DBG。
  • SVN 測試套件會自動關閉 SVN_DBG 輸出,若要手動抑制輸出,請在您的 shell 環境中將 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 伺服器邏輯;它作為 mod_dav 中的模組執行,而 mod_dav 則作為 httpd 中的模組執行。如果 httpd 可能使用動態共用模組,您可能需要在 mod_dav_svn 中設定中斷點之前 set breakpoint pending on(在 ~/.gdbinit 中)。或者,您可以啟動 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=https://127.0.0.1:3691/

在 Unix 上除錯 ra_svn 客戶端和伺服器

ra_svn 中的錯誤通常會顯示下列難以理解的錯誤訊息之一

  svn: Malformed network data
  svn: Connection closed unexpectedly

(第一個訊息也可能表示資料串流在隧道模式中因使用者點檔案或掛勾指令碼而損毀;請參閱 問題 #1145。)第一個訊息通常表示您必須除錯客戶端;第二個訊息通常表示您必須除錯伺服器。

使用 --disable-shared --enable-maintainer-mode 建置可以最輕鬆地除錯 ra_svn。使用後者選項時,錯誤訊息會精確告知您應在何行設定中斷點;否則,請在 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

然後執行有問題的客戶端命令。從那裡開始,就像除錯客戶端一樣。

在通道模式下除錯伺服器比較麻煩。您需要將類似「{ int x = 1; while (x); }」的東西貼在 svnserve 的 main() 頂端,並將產生的 svnserve 放到伺服器上的使用者路徑中。然後開始執行,gdb 附加伺服器上的程序,「設定 x = 0」,並按需要逐步執行程式碼。

追蹤網路流量

追蹤客戶端和伺服器之間的網路流量有助於除錯。有幾種方法可以進行網路追蹤(此清單並非詳盡無遺)。

執行網路追蹤時,您可能需要停用壓縮 — 請參閱 servers 設定檔中的 http-compression 參數。

使用 Wireshark 進行網路追蹤

使用 Wireshark(以前稱為「Ethereal」)竊聽對話。

首先,請確定在同一個 wireshark 會話中的擷取之間,您按了清除,否則來自一個擷取的篩選器(例如,HTTP 擷取)可能會干擾其他擷取(例如,ra_svn 擷取)。

假設您已清除,那麼

  1. 拉下擷取功能表,然後選擇擷取 篩選器

  2. 如果要偵錯 http:// (WebDAV) 協定,則在彈出的視窗中,選擇「HTTP TCP 埠 (80)」(這會產生篩選器字串「tcp port http」)。

    如果要偵錯 svn:// (ra_svn) 協定,則選擇新增,為新的篩選器命名(例如「ra_svn」),然後在篩選器字串方塊中輸入「tcp port 3690」。

    完成後,按一下確定。

  3. 再次前往擷取功能表,這次選擇介面,然後按一下適當介面旁的選項(您可能想要介面「lo」,代表「迴路」,假設伺服器會在與用戶端相同的機器上執行)。

  4. 取消勾選適當的核取方塊以關閉混雜模式。

  5. 按一下右下角的開始按鈕以開始擷取。

  6. 執行您的 Subversion 用戶端。

  7. 在作業完成時按一下停止圖示(乙太網路介面卡上方的紅色 X),或者擷取->停止應該會生效。現在您已完成擷取。它看起來像是一串很長的清單。

  8. 按一下通訊協定欄位以排序。

  9. 然後,按一下第一個相關的列以選取它;通常這會是第一個列。

  10. 按一下滑鼠右鍵,然後選擇追蹤 TCP 串流。您會看到 Subversion 用戶端 HTTP 轉換的請求/回應配對。

上述說明是針對 Wireshark 的圖形版本(版本 0.99.6),不適用於稱為「tshark」的命令列版本(對應到「tethereal」,源自 Wireshark 當時稱為 Ethereal 的時候)。

使用 socat 進行網路追蹤

另一種替代方案是在 Subversion 用戶端和伺服器之間設定一個記錄中繼伺服器。要這樣做的簡單方法是使用 socat 程式。例如,要記錄與 svnserve 執行個體的通訊,請執行下列指令

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

然後使用 svn://127.0.0.1:9630/ 的 URL 基礎執行您的 svn 指令;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

設定好此類代理後,您需要設定 Subversion 以使用代理。這可以使用 servers 設定檔中的 http-proxy-hosthttp-proxy-port 參數來完成。您也可以使用這些選項在命令列中指定代理 --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)
       

    您最關心的是第十個欄位(引號中的欄位),它會提供您建立此配置池的檔案和行號。前往該檔案和行,並判斷池的生命週期。在上述範例中,main.c:612 表示此配置是在 svn 客户端的頂層池中建立的。如果這是操作過程中重複多次的配置,這會指出記憶體外洩的來源。第十一個欄位(括號中的欄位)會提供配置本身的檔案和行號。