跳到主要內容

善用 strace、debugger 從執行期間找出問題根源

最近被迫在短時間內學會 strace、gdb 這些之前一直用不到的重兵器, 都還不熟練就是了。剛好使用 hgsubversion 時有些困擾, 雖說它和 svn 整合得很好, 無縫接好 pull / push, 但它不會顯示 mercurial 對應到的 svn 版本, 平時看其它和 svn 整合的工具 (如 issue tracking) 會很困擾, 用得都是 svn 版號。

剛剛想到可以學 Strace -- The Sysadmin's Microscope 的做法, 用 strace 找出關聯的程式, 再來看怎麼修正它。

我推測 hg 一定有記錄 svn 相關版本的方式, 不然無法和 svn server 同步資料。於是挑個會讀到 svn 資料的指令來試:

strace -o trace.log -s 512 -e read,open hg svn info

用顯示的 svn 版號 123 來翻 trace.log, 發現這兩行:

open("/path/to/project/.hg/svn/lastpulled", O_RDONLY) = 3
read(3, "123\n", 4096)                 = 5

於是找到 meta data 存在 .hg/svn/ 下。 到該目錄下找到 .hg/svn/rev_map 這個檔案, 裡面存 hg 和 svn 的版號對應表。至少這樣就有足夠的材料寫個 script 來轉換 hg、svn 的版號。

不過若能直接加到 hg 裡, 應該會更方便也較可攜。要做這點相對容易, 可以到 hgsubversion 原始碼目錄下用 ack 找相關程式。

先用 ack rev_map 找到存 meta data 的物件 revmap, 再用 ack revmap 找到 wrappers.py 是換掉 hg 指令的程式。

再來用 pdb 觀察 revmap 如何被使用。先在 wrappers.py 裡設中斷點, 然後執行 pdb /usr/local/bin/hg parents --svn 找出 meta data 如何被讀出來。於是明白可在函式 parents 的部份塞入幾行顯示 svn 版本:

--- a/hgsubversion/wrappers.py        2011-12-25 00:34:39.170606104 +0800
+++ b/hgsubversion/wrappers.py        2011-12-25 00:33:04.161800527 +0800
@@ -57,6 +57,9 @@
         raise hgutil.Abort('No parent svn revision!')
     displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False)
     displayer.show(ha)
+    # patch svn revision
+    print '\033[1;32msvn revision: %d\033[0m' % hashes[ha.node()][0]
+    print
     return 0

這樣打 hg parents --svn 就會多輸出一行 svn 的版號。雖說顯示在 hg log 會更方便, 不過 wrappers.py 裡沒有 log, 之後有再找時間看看怎麼加。

附帶一提, 剛用 pdb 時還不太習慣, 忘了 python 是執行期載入程式, 不能像 gdb 那樣在執行前就指定檔名指定行數設中斷點。而是要在 script 裡直接塞

import pdb; pdb.set_trace()

2011-12-25 更新

如留言裡的討論, 上述的修正沒有實質幫助, 就當作練 debugger 吧。最後覺得另外寫個 shell script 最省事效果也最好, 寫好的東西放在這裡

留言

  1. Hey學長,我最近也打算架設一個紀錄學習心得的blog,能否請教你我該怎麼把code/command貼上來(syntax highlighter?)

    回覆刪除
  2. @Heron 你是打算用blogger, wordpress or 自己架設?
    不然google search code highlight or syntax highlight.
    ex:
    http://heisencoder.net/2009/01/adding-syntax-highlighting-to-blogger.html

    回覆刪除
  3. 工具用的好。strace 最好用的功能之一就是『程式到底在讀哪個檔案』,而只 trace syscall 剛好能看到此重點。你這樣短時間內改別人程式的能力應感覺到有提昇?

    你的 use case 我有點疑惑:
    打 "hg svn info" 看 SVN revision 跟打 "hg parents --svn" 不是差不多? 若是看 "hg log" 時需要 SVN rev 我還能理解.

    gdb 下也可像 "pdb.set_trace()" 一樣在 source 中插入中斷: https://github.com/scottt/debugbreak
    gdb 除錯的對象有用 shared library 時,往往也是『執行期載入程式』。pdb 不能用檔名、行數預設 breakpoint 可算是缺功能。我自己最常用的其實是將 ipython 類似 "pdb.set_trace()"來用:
    import IPython; IPython.embed()

    (ipython 0.10 後的 API)


    另外也鼓勵你對 hgsubversion 從 bitbucket.org 回報 bug 、 merge request。

    我有寫過一個小程式,像是一個特殊功能的 debugger 是當 debug 的 target 發出 syscall (用 PTRACE_SYSCALL) 時就印出 stack trace (用 libunwind)。我整理一下,下次見面時跟你分享。

    回覆刪除
  4. @Heron: 在這頁 view-source,然後找 Google Code Prettify.類似的程式不少

    回覆刪除
  5. 恩,我弄好了,本來想使用新版Blogger的動態檢視,卻發現他限制更改html。接下我也要來好好學習寫好的Blog了。
    http://heron-note.blogspot.com/

    回覆刪除
  6. @Heron 順便廣告一下不直接相關的事, 我都是用自己寫的工具發文 http://fcamel-fc.blogspot.com/2011/06/ego-post-wsyiwyg-wiki.html

    這東西可以直接改成 Chrome Web App, 這樣別人要用按個安裝就結束了。不過目前自己用得好好的, 暫時也沒時間移過去, 所以就一直拖著 ...

    回覆刪除
  7. @Scott 這個 use case 的確有問題, 應該要直接改 log 才對。我原本是看到 parents 有 --svn 的功能, 至少可克難的用 hg parents -r REV --svn 查對應, 不過目前的程式這部份並沒有用, 所以就是.....還沒達到目的, 接下來有空會再看怎麼改到 log 裡, 然後問看看該社群有沒有興趣接收。

    目前想到兩個用法:
    1. 加個新指令 hg svn lookup 查 svn -> hg 的對應, 改起來簡單, 用法也不會令人困惑

    2. 加上 hg log --svn, 首先得先定義這個 --svn 的行為為何, 還有顯示的方式。定規格部份就滿麻煩的, 也得另外定顯示方式

    所以應該會先試 1 吧

    回覆刪除
  8. 目前覺得進步滿多的, 不過離能應付實戰還有段距離。待能順手應付實戰後, 應該會成長不少吧 XD

    下個目標是加強使用 gdb 的能力, 再來則是補充 linux system programming 的常識

    回覆刪除
  9. svn2hg line 40 指定 hg_revision 中,grep 前面多了反斜線? 類似 svn2hg 這種 script,需要 option parsing 時我後來都用 Python 寫。

    回覆刪除
  10. 也是, 我想說只有一個參數就將就一下, 再多一點用 python 比較方便。

    那個反斜線表示不要用 alias 展開的結果, 就叫 shell 執行 "grep"。我平時有設 alias grep='grep --color=auto'

    回覆刪除
  11. @fcamel: Re: svn2hg alias
    I see. 但讓 grep alias 展開應該無妨?

    回覆刪除
  12. 以前踩過雷, 有時寫 shell script 就會順手加。經你這樣一提, 才想起來了, 是 ls 有影響, grep 到是沒差。

    我的 ls 是 alias ls='ls -F --color', 配合 for loop 取檔名時會變成拿到錯誤的檔名

    回覆刪除
  13. ls --color=auto 就會在只有 isatty(stdout) 時才輸出控制碼

    回覆刪除
  14. 但是 pipe 後也會消失, 有時會 ls | less, 希望保有顏色, 還有我習慣加 -F。所以就變成 alias ls 設成那樣, 寫 shell script 時記得寫 \ls 了

    回覆刪除
  15. Re: ls --color:
    這樣有一天別人的 shell script 只有在你的系統上有問題的時候你要記得檢查 ls 部份啊 ... ;)

    回覆刪除

張貼留言

這個網誌中的熱門文章

(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫

以使用 LevelDB 為例。 抓好並編好相關檔案,編譯方式見第三方函式庫附的說明:$ ls include/ # header files leveldb/ $ ls out-shared/libleveldb.so* # shared library out-shared/libleveldb.so@ out-shared/libleveldb.so.1@ out-shared/libleveldb.so.1.20* 下面的例子用 clang++ 編譯,這裡用到的參數和 g++ 一樣。 問題一:找不到 header$ clang++ sample.cpp sample.cpp:5:10: fatal error: 'leveldb/db.h' file not found #include "leveldb/db.h" ^ 1 error generated. 解法:用 -I 指定 header 位置 問題二:找不到 shared library$ clang++ sample.cpp -I include/ /tmp/sample-2e7dd8.o: In function `main': sample.cpp:(.text+0x1e): undefined reference to `leveldb::Options::Options()' sample.cpp:(.text+0x6f): undefined reference to `leveldb::DB::Open(leveldb::Options const&, std::string const&, leveldb::DB**)' sample.cpp:(.text+0x10c): undefined reference to `leveldb::Status::ToString() const' sample.cpp:(.text+0x7d0): undefined reference to `leveldb::Status::ToString() const' clang: error: linker command failed with exit code 1 (u…

熟悉系統工具好處多多

記一下以前很困擾, 現在秒殺的小事。 更新這篇的時候, 忘了函式庫用的 man page 裝在那個 package。以前就會想辦法 google, 運氣好一下會找到, 運氣不好會多找一會兒。 這回我想到新作法:$ strace -e open man 3 printf > /dev/null # 發現是讀 /usr/share/man/man3/printf.3.gz $ dpkg --search /usr/share/man/man3/printf.3.gz # 找到套件名稱 manpages-dev $ aptitude show manpages-dev # 確認描述符合, 收工

virtualbox 使用 USB 裝置

2012-12-16 更新 現在 (4.x 版) 似乎無需做任何設定, 只要有裝 Oracle VM VirtualBox Extension Pack, 在 VirtualBox 視窗右下角按 USB 的圖示, 再點目標裝置, 即可加入或移除該裝置 同一時間只有 host 或 guest 可擁有該裝置, 所以從 guest OS 移除, 相當於接回 host OS 目前 VirtualBox 只支援 USB 2.0 的插槽, 若偵測不到時, 注意一下是否為這個問題 有時拔拔插插, VirtualBox 會進入奇怪的狀態, 接上去 guest OS 無法連接且跳出 device is busy 的錯誤訊息。試看看拔除該裝置, 重開 guest OS (續上則) 若重開 guest OS 無效, 並且 host OS 已移除該裝置, VirtualBox 的 USB 清單卻仍顯示 "captured", 試看看拔除該裝置, 重開 host OS原文網路上搜一下, 比較多是 Ubuntu 當 host 的解法, 我的情況是 Win7 當 host, Ubuntu 當 guest。 這兩篇說明很詳細《Learn How to Set Up USB and Networking Options in VirtualBox》《幻影千瞳的部落格: VirtualBox 使用筆記(二):使用 USB 裝置》 現在的版本圖形介面很好用了, 不用像第二篇說的那樣用指令操作。這裡記下我的操作步驟: 關掉 guest OS 在 VirtualBox 選單, 選擇 guest OS -> Settings -> USB -> Enable USB 2.0 會出現訊息框, 說明要安裝 Oracle VM VirtualBox Extension Pack。下載後安裝它 host OS 插入 USB 隨身碟 在 VirtualBox 選單, 選擇 guest OS -> Settings -> USB, 點右邊有綠色 "+" 的 USB 頭的圖示, 選擇該 USB 隨身碟, 加入它的 filter 從 host OS 移除 USB 隨身碟 開啟 guest OS 插入 USB 隨身碟, 於是 guest OS 會自動偵測…