預設的 mail 不方便夾帶附加檔, 備忘一下用 mutt 做這事的指令:
mutt -s TITLE EMAIL -a ATTACHMENT < CONTENT
預設的 mail 不方便夾帶附加檔, 備忘一下用 mutt 做這事的指令:
mutt -s TITLE EMAIL -a ATTACHMENT < CONTENT
在用 Bottle 的開發模式時, 發覺有時候速度會異常的慢。等到受不了以後, 按下 ctrl+c 看到以下的 backtrace:
Exception happened during processing of request from ('xxx.xxx.xxx.xxx', 37515) Traceback (most recent call last): File "/usr/lib/python2.7/SocketServer.py", line 284, in _handle_request_noblock self.process_request(request, client_address) File "/usr/lib/python2.7/SocketServer.py", line 310, in process_request self.finish_request(request, client_address) File "/usr/lib/python2.7/SocketServer.py", line 323, in finish_request self.RequestHandlerClass(request, client_address, self) File "/usr/lib/python2.7/SocketServer.py", line 638, in __init__ self.handle() File "/usr/lib/python2.7/wsgiref/simple_server.py", line 121, in handle self.rfile, self.wfile, self.get_stderr(), self.get_environ() File "/usr/lib/python2.7/wsgiref/simple_server.py", line 85, in get_environ host = self.address_string() File "/usr/lib/python2.7/BaseHTTPServer.py", line 498, in address_string return socket.getfqdn(host) File "/usr/lib/python2.7/socket.py", line 137, in getfqdn hostname, aliases, ipaddrs = gethostbyaddr(name) KeyboardInterrupt
依照過去的經驗, 這個 gethostbyaddr 相當可疑。
作個簡單測試證實問題:
$ python -c 'import socket; print socket.gethostbyaddr("xxx.xxx.xxx.xxx")' Traceback (most recent call last): File "<string>", line 1, in <module> socket.herror: [Errno 2] Host name lookup failure
再來用老招 ltrace 確認問題源頭:
$ ltrace python -c 'import socket; print socket.gethostbyaddr("xxx.xxx.xxx.xxx")' 2>&1 | grep gethost ( ... 略 ... ) gethostbyaddr_r(0x7fffa5bbb834, 4, 2, 0x7fffa5bb77f0, 0x7fffa5bb7830) = 0
由 man gethostbyaddr 得知它會查 /etc/hosts, 最後在 /etc/hosts 加入一筆 "xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx", 就立即有結果了:
$ python -c 'import socket; print socket.gethostbyaddr("xxx.xxx.xxx.xxx")' ('xxx.xxx.xxx.xxx', [], ['xxx.xxx.xxx.xxx'])
今天遇到一個神奇的 bug, 程式掛掉後, 從 backtrace 看到其中一個函式名稱為 __cxa_pure_virtual, 從字面上的意思來看, 我呼叫了一個 pure virtual function, 所以程式掛了。但是 compiler 應該能抓到在 C++ 內呼叫 pure virtual function (Btw, Objective C++ 則否), 沒道理在 runtime 遇到這情況。
google 了一陣子, 看到這篇說有可能是呼叫 dangling pointer 的 virtual function, "Pure Virtual Function Called": An Explanation 則解釋各種可能的觸發原因。最後抓到兇手, 確實是呼叫 dangling pointer 的 virtual function。
依文章的解釋, destructor 會改變 virtual method table pointer 指向的位置, 最後會指向 abstract base class 的 virtual table, 裡面的 virtual function 則指向某個表示 pure virtual function 的指標 (gcc 用__cxa_pure_virtual)。所以, 若刪除指標後剛好記憶體內容保持原樣, 執行後就會呼叫到 __cxa_pure_virtual。
依管理記憶體的方式, 刪除指標時可能會保留原狀, 也可能填入特殊值故意觸發 crash。呼叫 dangling 可能會有不同結果。
首先要定義一下什麼是「memory leak」。它的正式定義是指「程式弄丟了未釋放空間的位置」, 之後無法使用這塊空間也無法釋放它。另一方面, 程式沒有弄丟位置, 但是配置的空間愈用愈大, 或是用完後沒有歸還, 導致記憶體不足。若要應付第一種情況, 可用 memory leak 作為關鍵字找相關討論; 第二種情況要用 memory profiler 或 heap profiler 了解記憶體用去那裡, ( 感謝 Scott 告知 )。
就我自己少量的經驗來說, 第二種比較棘手, 第一種通常是粗心, 找到後補上 free/delete 即可。但第二種可能和程式配置和運用記憶體的策略有關。我遇到的問題都是第二種。若程式有針對不同區塊自己管記憶體的話, 比較方便日後植入統計程式找出瓶頸。順道一提, 如同 TDD 的精神, 在設計程式架構時就考慮測試, 會讓程式更易測試、維護和除錯。在設計架構時就考慮除錯, 也會讓程式更易維護和除錯。
在 Linux 上試了兩套工具 mtrace 和 valgrind。mtrace 滿簡單的, 可用來抓 C 的 memory leak, 但不適用於 C++, 原因是 mtrace 在 malloc/free 動手腳, 可以找出那一行呼叫 malloc 後沒呼叫 free。但是 C++ 的情況是 libstdc++ 呼叫 malloc, 而我們有興趣的是誰呼叫了 new。
後來改試 valgrind 效果還不錯。valgrind 是多個除錯工具的集合體, 我試了兩個工具:
使用 valgrind 的代價是程式會慢個 20 倍左右。但是用工具協助檢測問題, 99% 的情況會比自己憑空猜想來得準。還是有些耐性使用 valgrind 會比較快找到問題源頭。
memcheck$ valgrind --tool=memcheck --leak-check=full PROG PROG_ARG ...
跑完後會輸出結果在 console。建議先有些耐心看完官網文件。對 memcheck 的報表有疑問的話, 可參考 "Understanding Valgrind memory leak reports"。重點是 possibly leak 表示 valgrind 無法判斷它是 still reachable 或 definitely leak。still reachable 可能是某些函式庫配置自己一塊 memory pool 來用, 程式離開時才會釋放, 所以嚴格說起來不是 memory leak。在這個階段, 我們關心的是 definitely leak。
massif
$ valgrind--tool=massif PROG PROG_ARG ...
跑完後會輸出結果在 massif.out.PID。然後執行 ms_print 看結果
$ ms_print massif.out.PID
建議先有些耐心看完官網文件。我試了許多參數想抓到程式離開前的 detailed snapshot 都徒勞無功。最後發覺用 vgdb 意外的簡單。
用法如下:
valgrind 也可以和 gdb 一起互動, 沒想到會在這個機會下試用到 gdb 的 remote debugging 功能。
經 Scott 告知, 除了用 ms_print 看文字報表外, 也可用 Massif Visualizer 看結果。這工具看起來滿不錯的, 先備忘在這裡。
另外補充一下 Linux 下記憶體用量的意思。平常我們是看 ps 回報的 VSZ 和 RSS, 但兩者其實都不夠準確, 用 pmap -d PID 看最後一行輸出的 private writable memory, 可能比較適合。
就用 Mac OS X 附的 Instrutments, 使用 Leak 或 Allocations, 不用看文件即可輕鬆搞定! 全圖形介面操作, 還有詳細的報表。要看各 functions 用掉 heap 記憶體的比例的話, 在 Allocations 裡點 Statistics -> Call Tree。
同事用過 Instruments 後, 甚至說「真想將 Linux 的程式 port 到 Mac, 這樣才可以用 Instruments」, 由此可知 Instruments 有多麼地好用。
Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...