在Linux下調試Python代碼的各類方法

這是一個我用於調試或分析工具概述,不必定是完整全面,若是你知道更好的工具,請在評論處標記。node

日誌python

是的,的確,不得不強調足夠的日誌記錄對應用程序是多麼的重要。您應該記錄重要的東西,若是你的記錄足夠好的話,你能夠從日誌中找出問題從而節省大量的時間。ios

若是你曾經用print語句來調試代碼如今停下吧,用logging.debug替代,開始能夠慢慢來,之後徹底禁用它...git

追蹤
有時看到程序如何被執行會頗有幫助。你可使用IDE的調試共軛ngn一步一步的運行程序,但你須要知道你要找的是什麼,不然這將會是一個漫長的過程。
標準庫中有一個 trace模塊,能夠打印全部執行過程當中的內容(像製做 覆蓋率報告)。
 github

?
1
python -mtrace --trace script.py

這將產生大量輸出(每一個行會被打印輸出,因此你最好經過管道,用grep只看本身感興趣的部分),例如:
 shell

?
1
2
python -mtrace --trace script.py | egrep '^(mod1.py|mod2.py)'
-

若是你喜歡新特性,那麼你能夠嘗試 smiley - 它能夠顯示變量內容變化,還能夠用它來遠程追蹤程序。bash

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鍵啓動腳本):
 session

?
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
  
...
相關文章
相關標籤/搜索