這是一個我用於調試或分析工具概述,不一定是完整全面,如果你知道更好的工具,請在評論處標記。
日志
是的,的確,不得不強調足夠的日志記錄對應用程序是多么的重要。您應該記錄重要的東西,如果你的記錄足夠好的話,你可以從日志中找出問題從而節省大量的時間。
如果你曾經用print語句來調試代碼現在停下吧,用logging.debug替代,開始可以慢慢來,以后完全禁用它...
追蹤
有時看到程序如何被執行會很有幫助。你可以使用IDE的調試共軛ngn一步一步的運行程序,但你需要知道你要找的是什么,否則這將會是一個漫長的過程。
標准庫中有一個 trace模塊,可以打印所有執行過程中的內容(像制作 覆蓋率報告)。
1
|
python -mtrace --trace script.py
|
這將產生大量輸出(每個行會被打印輸出,所以你最好通過管道,用grep只看自己感興趣的部分),例如:
1
2
|
python -mtrace --trace script.py |
egrep
'^(mod1.py|mod2.py)'
-
|
如果你喜歡新特性,那么你可以嘗試 smiley - 它可以顯示變量內容變化,還可以用它來遠程追蹤程序。
PDB
1
2
|
import
pdb
pdb.set_trace()
# opens up pdb prompt
|
或者:
1
2
3
4
5
6
7
|
try
:
code
that
fails
except
:
import
pdb
pdb.pm()
# or pdb.post_mortem()
|
或(按鍵盤C鍵啟動腳本):
1
|
python -mpdb script.py
|
像在REPL中那樣:
- c or continue
- q or quit
- l or list, 在當前界面顯示源碼
- w or where, 顯示回溯
- d or down, 顯示回溯的下一界面
- u or up, 顯示回溯的上一界面
- <enter>, 重復最后一個命令
- 其他任何東西,在當前界面評估源碼 (t還有其他的一些命令)
- corcontinue
- qorquit
- lorlist,顯示在當前幀的源
- worwhere,顯示回溯
- dordown,下山1幀回溯
- uorup,上升1幀回溯
- 回車,重復最后一個命令
幾乎任何東西,評估當前幀的Python代碼(還有其他幾個命令)
可以替代pdb的:
- ipdb (easy_install ipdb) - 像 ipython (自動補齊, 顏色等)
- pudb (easy_install pudb) - 基於curses (類gui), 瀏覽源碼有很好的表現。
遠程 PDB
1
|
sudo
apt-get
install
winpdb
|
替代 pdb.set_trace():
1
2
|
import
rpdb2
rpdb2.start_embedded_debugger(
"secretpassword"
)
|
現在運行Winpdb , 輸入密碼 到 File > Attach。
不喜歡 Winpdb ? 只要通過 TCP運行 PDB
使用下面代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
import
loggging
class
Rdb(pdb.Pdb):
"""
This will run pdb as a ephemeral telnet service. Once you connect no one
else can connect. On construction this object will block execution till a
client has connected.
Based on https://github.com/tamentis/rpdb I think ...
To use this::
Rdb(4444).set_trace()
Then run: telnet 127.0.0.1 4444
"""
def
__init__(
self
, port
=
0
):
self
.old_stdout
=
sys.stdout
self
.old_stdin
=
sys.stdin
self
.listen_socket
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self
.listen_socket.bind((
'0.0.0.0'
, port))
if
not
port:
logging.critical(
"PDB remote session open on: %s"
,
self
.listen_socket.getsockname())
print
>> sys.__stderr__,
"PDB remote session open on:"
,
self
.listen_socket.getsockname()
sys.stderr.flush()
self
.listen_socket.listen(
1
)
self
.connected_socket, address
=
self
.listen_socket.accept()
self
.handle
=
self
.connected_socket.makefile(
'rw'
)
pdb.Pdb.__init__(
self
, completekey
=
'tab'
, stdin
=
self
.handle, stdout
=
self
.handle)
sys.stdout
=
sys.stdin
=
self
.handle
def
do_continue(
self
, arg):
sys.stdout
=
self
.old_stdout
sys.stdin
=
self
.old_stdin
self
.handle.close()
self
.connected_socket.close()
self
.listen_socket.close()
self
.set_continue()
return
1
do_c
=
do_cont
=
do_continue
def
set_trace():
"""
Opens a remote PDB on first available port.
"""
rdb
=
Rdb()
rdb.set_trace()
|
想要 REPL ? IPython 怎么樣?
如果你不需要一個整體的調試器,只要啟動IPython用下面的代碼:
1
2
|
import
IPython
IPython.embed()
|
標准Linux工具
他們未被充分利用很令我驚訝。通過這些工具集你能弄清楚諸如這些的很多問題:從性能問題(太多的系統調用,內存分配等)到死鎖,網絡,磁盤等問題。
1
2
|
sudo
apt-get
install
htop
sudo
htop
|
最有用的是降權運行strace,只需運行速凍 strace -P 12345 或strace-f 命令參數(-f表示strace分支進程)。 通常有很多的輸出,你最好將輸出重定向輸出到一個文件(命令后添加 &> 文件名)來進行更深入的分析。
然后就是ltrace,它和strace相似不過是通過庫調用的,參數基本相同。
lsof可以提供 你看過ltrace/ strace的處理號,這樣使用:lsof -P 12345
讓跟蹤更深點
它很容易使用以及可以做很多事,前提是大家都已經安裝了htop!
現在,找你所想的進程,僅僅需要按:
- s 顯示系統調用跟蹤(strace)
- L 顯示庫調用跟蹤(ltrace)
- l 顯示lsof
監視
沒有更好的替代品了,服務器持續監視,你曾經是否發現自己使用奇奇怪怪的跟蹤方法去找出為什么哪里慢了以及資源怎么被消耗了,那么不要再被iotop, iftop, htop, iostat, vmstat等等煩擾了,趕快使用dstat吧,它可以做大多數上述的提到的工具能做的,而且可以做得更好!
它會以緊湊,時尚的代碼着色(親,不像iostat, vmstat喲)持續顯示你的數據,而且你可以一直看到以往的數據(與iftop, iotop, htop不同喲)。
僅僅運行這個:
1
|
dstat --cpu --io --mem --net --load --fs --vm --disk-util --disk-tps --freespace --swap --
top
-io --
top
-bio-adv
|
還有一點就是這里還有更簡單的方式來寫喲,如shell歷史記錄(shell history)或則重命名命令(aliases)
GDB
這是一個相當復雜和強大的工具,但我僅僅涉及到基礎的東西(設置和基本命令)。
1
2
3
4
|
sudo
apt-get
install
gdb python-dbg
zcat
/usr/share/doc/python2
.7
/gdbinit
.gz > ~/.gdbinit
run app with python2.7-dbg
sudo
gdb -p 12345
|
現在請使用:
bt- 堆棧軌跡(C 級)
pystack- python 堆棧軌跡,前提是你需要擁有~/.gdbinit 並使用python-dbg
c(繼續)
有出現 segfaults 么 ?用 faulthandler !
除了Python 3.3其他的都會出現這個可怕的錯誤, 回到Python 2.x
只要按照下面來做,你至少會找到一條導致段錯誤的原因。
1
2
|
>>>
import
faulthandler
>>> faulthandler.enable()
|
內存泄露
好的,這里有許多工具,其中有一些是專門用於WSGI 應用的,像Dozer,但是我最喜歡的無疑是 objgraph。它是如此驚人的方便和易於使用。它沒有與WSGI或任何其他東西繼承,所以你需要找到你自己的方式來運行以下代碼:
1
2
3
4
5
|
>>>
import
objgraph
>>> objs
=
objgraph.by_type(
"Request"
)[:
15
]
>>> objgraph.show_backrefs(objs, max_depth
=
20
, highlight
=
lambda
v: v
in
objs, filename
=
"/tmp/graph.png"
)
Graph written to
/
tmp
/
objgraph
-
zbdM4z.dot (
107
nodes)
Image generated as
/
tmp
/
graph.png
|
你會得到一個像 這樣的圖表(警告:這個圖表非常大)。你也會得到 dot輸出。
內存利用
有時你想使用更少的內存。少分配內存通常會使程序運行的更快更好,用戶們都喜歡精益求精:)
有許多工具可以拿來使用 [1] ,但在我看來最好的是pytracemalloc - 與其他工具相比較,它的開銷很小(不需要依賴於削弱速度的 sys.settrace)並且它的輸出非常詳盡。令人頭疼的是它的配置,因為需要你重編譯python,但是spt使其很容易做到。
只要運行以下命令,然后你就可以去買午餐或者做其他事了:
1
2
3
4
5
|
apt-get
source
python2.7
cd
python2.7-*
wget https:
//github
.com
/wyplay/pytracemalloc/raw/master/python2
.7_track_free_list.patch
patch -p1 < python2.7_track_free_list.patch
debuild -us -uc
cd
..
sudo
dpkg -i python2.7-minimal_2.7*.deb python2.7-dev_*.deb
|
然后安裝pytracemalloc(請注意:如果你是在虛擬環境中做的這些操作,那么在python重新安裝后,你需要重建它-僅運行virtualenv myenv即可):
1
|
pip
install
pytracemalloc
|
1
|
|
現在,你就可以通過以下代碼來封裝你的應用程序:
1
2
3
4
5
6
7
8
9
10
11
|
import
tracemalloc, time
tracemalloc.enable()
top
=
tracemalloc.DisplayTop(
5000
,
# log the top 5000 locations
file
=
open
(
'/tmp/memory-profile-%s'
%
time.time(),
"w"
)
)
top.show_lineno
=
True
try
:
# code that needs to be traced
finally
:
top.display()
|
會得到像下面這樣的輸出:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
2013
-
05
-
31
18
:
05
:
07
: Top
5000
allocations per
file
and
line
#1: .../site-packages/billiard/_connection.py:198: size=1288 KiB, count=70 (+0), average=18 KiB
#2: .../site-packages/billiard/_connection.py:199: size=1288 KiB, count=70 (+0), average=18 KiB
#3: .../python2.7/importlib/__init__.py:37: size=459 KiB, count=5958 (+0), average=78 B
#4: .../site-packages/amqp/transport.py:232: size=217 KiB, count=6960 (+0), average=32 B
#5: .../site-packages/amqp/transport.py:231: size=206 KiB, count=8798 (+0), average=24 B
#6: .../site-packages/amqp/serialization.py:210: size=199 KiB, count=822 (+0), average=248 B
#7: .../lib/python2.7/socket.py:224: size=179 KiB, count=5947 (+0), average=30 B
#8: .../celery/utils/term.py:89: size=172 KiB, count=1953 (+0), average=90 B
#9: .../site-packages/kombu/connection.py:281: size=153 KiB, count=2400 (+0), average=65 B
#10: .../site-packages/amqp/serialization.py:462: size=147 KiB, count=4704 (+0), average=32 B
...
|