程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

使用gdb調試Python程序

編輯:Python

前言

不知各位有木有遇到這樣的情況,生產環境下的Python進程突然卡死了,所有其他線程都無法調度,如果我們kill掉重啟,通常會喪失掉當前報錯的上下文信息,失去這些信息,對後續報錯定位不太友好,能不能在不關停Python程序的情況下,看看程序發生了什麼問題?

首先,你可以嘗試一下,此前我提過的py-spy(漫畫:如何分析運行中的 Python 程序?),py-spy可以打印出簡單的調用信息,但很多時候不夠用,這裡我們通過gdb來調試Python程序,同時打印c棧和py棧的信息,調試起來,一目了然。

gdb主要用於調試c/c++程序的,因為Python是使用c寫的(cpython,當然還有其他語言實現的),對gdb而言,將python當成普通的c程序調試則可。

當我們使用gdb調試python進程時,我們會獲得解釋器級別的調試信息和內存狀態,而不是應用程序級別的(我們使用pdb便是應用程序基本的),這樣我們可以看到Python完整的執行流程,包括解釋器上函數與變量的信息。

我的服務器是Ubuntu 20.04,所以本文的操作都在Ubuntu 20.04上進行。

安裝gdb調試環境

首先,需要安裝一下gdb。

sudo apt-get install gdb

光安裝gdb,在調試python時,雖然可以用,但不太友好,為了更好的浏覽調試信息,我們為cpython安裝gdb debugging Symbols。

gdb中的debugging Symbols的主要作用是將程序編譯後的二進制指令映射到源代碼相應的變量、函數和行中,這樣在調試時可以很好的浏覽調試信息。

在Ubuntu中,安裝python-dbg則可。

sudo apt-get install python-dbg

python-dbg提供了gdb Debugging Symbols和一些調試python的命令,如py-bt、py-list,本文後續會使用。

python-dbg之所以可以發揮作用,是因為它將python gdb相關的內容自動復制到gdb auto-load目錄下了,這樣gdb在啟動時,會自動加載這些內容。

你可以在進入gdb交互命令後,通過info auto-load來查看相關信息。

調試Python程序

安裝好gdb調試環境後,准備一段代碼,簡單使用一下gdb。

我在當前用戶目錄下,創建了play_gdb目錄並在其中創建了虛擬環境,然後在目錄中創建了play_gdb.py,裡面就是一段簡單的計算斐波那契數列的代碼。

在日常開發中,我們經常使用python虛擬環境,所以這裡想測試一下,如果我們使用python虛擬環境運行程序時,gdb是否可以正常調試。

在使用python venv前,先通過系統的python來試試,不進入虛擬環境,直接運行。

從上圖可知,我們使用系統python運行程序,開啟新窗口,通過gdb調試一下,如下圖:

上圖命令為:

ps -x | grep python
sudo gdb -p 1199469

先找到pid,然後再讓gdb直接附加到運行中的python進程上。

通過bt命令,查一下當前程序的調用棧。

從上圖可以看出,有很多python解釋器級別的打印,我們看到程序目前在python的timemodule.c的pysleep方法中,最終調用了linux系統的select.c(即通過I/O復用相關的邏輯來實現python進程中主線程的sleep)。

gdb的bt命令可以將c調用棧完整打印出來,如果我們只想看python調用棧,可以使用py-bt(你需要安裝python-dbg才能用),此外,如果想查看當前程序的py代碼,可以使用py-list(等價pdb的ll命令),如下圖:

從上圖看,py-bt和py-list都沒有正常運行,這是因為我們使用gdb時,沒有在當前項目的根目錄,通過q命令退出一下,然後進入項目根目錄,再用gdb開啟調試。

簡單列一下gdb調試時的常用命令:

bt    # 當前C調用棧
py-bt  # 當前Py調用棧
py-list  # 當前py代碼位置
py-up  # 上一幀(py級別的幀)
py-down  # 下一幀(py級別的幀)
info thread   # 線程信息
thread <id>   # 切換到某個線程
thread apply all py-list  # 查看所有線程的py代碼位置
ctrl-c  # 中斷

更多用法可以看pthon官方關於gdb的文檔:https://devguide.python.org/gdb/

如果利用python虛擬環境中的python解釋器來執行py程序,gdb也可以正常調試,沒有什麼使用上的差異。

美化gdb調試信息

通過gdb-dashboard(https://github.com/cyrus-and/gdb-dashboard)可以美化gdb調試信息,從而增加效率。

從gdb7開始,gdb便支持使用Python代碼來擴展gdb,gdb-dashboard的原理正是如此。

在當前使用gdb的用戶目錄下,下載gdb-dashboard提供的.gdbinit文件,你可以直接從github中拉取,然後復制到相應的用戶目錄下。

然後你再安裝一下pygments,用於啟用語法高亮顯示的效果(選擇性安裝):

pip install pygments

因為我是通過root用戶來使用gdb的,所以需要將.gdbinit放置在root用戶目錄下。

然後正常使用gdb,會獲得下圖效果。

上圖中,可以很直觀地看到調用棧(Stack)、變量(Variables)、寄存器(Registers)等信息,都是c層面的。

在gdb中,通過help dashboard可以查看dashboard更多的用法。

結尾

掌握gdb調試,可以更好地理解python源碼,對於一些比較難搞的線上情況,也更加游刃有余,學起來呗。

參考

  • 使用gdb調試CPython進程 (https://github.com/ictar/python-doc/blob/master/Others/%E4%BD%BF%E7%94%A8gdb%E8%B0%83%E8%AF%95CPython%E8%BF%9B%E7%A8%8B.md)

  • gdb調試cpython(https://meteorix.github.io/2019/02/13/gdbpython/)


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved