CPython 程式開發過程中,除錯與效能分析至關重要。本文將介紹如何使用 Python 內建的 unittest
框架撰寫測試案例,並示範如何使用 LLDB 和 GDB 這兩種強大的除錯工具,逐步執行程式碼、檢查變數與設定斷點,找出程式碼中的錯誤。此外,文章也會說明如何使用 timeit
進行微型效能測試,以及如何利用 pyperformance
工具比較不同 Python 版本的效能差異,搭配 cProfile
進行程式碼執行時間分析,找出效能瓶頸。最後,文章也將介紹如何在 Windows、macOS 和 Linux 平臺上編譯 CPython 以支援除錯功能,並示範如何使用 faulthandler
模組處理 segmentation fault 等錯誤。
範例:Unicode測試
以下是Unicode測試類別的一個範例:
import unittest
class UnicodeTest(unittest.TestCase):
def test_casefold(self):
self.assertEqual('hello'.casefold(), 'hello')
self.assertEqual('hELlo'.casefold(), 'hello')
這個範例定義了一個UnicodeTest
類別,包含一個test_casefold
方法。這個方法使用斷言來驗證casefold()
方法的行為是否符合預期。
Python 測試框架與除錯工具
Python 的測試框架提供了一個強大的工具來確保程式碼的正確性和可靠性。其中,unittest
模組是 Python 標準函式庫中的一部分,提供了豐富的測試功能。
測試框架
Python 的測試框架允許您建立和執行測試使用案例,以驗證您的程式碼是否按照預期執行。您可以使用 assert
陳述式來檢查程式碼的行為是否正確。
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
if __name__ == '__main__':
unittest.main()
除錯工具
Python 的除錯工具可以幫助您找出程式碼中的錯誤和問題。其中,pdb
模組是一個內建的除錯工具,允許您逐步執行程式碼、檢查變數和設定斷點。
import pdb
def add(a, b):
pdb.set_trace()
return a + b
result = add(2, 3)
print(result)
LLDB 和 GDB
LLDB 和 GDB 是兩個流行的除錯工具,分別適用於 macOS 和 Linux 平臺。它們提供了更強大的功能,例如反組譯、記憶體檢視和多執行緒除錯。
# LLDB
lldb python
# GDB
gdb python
Visual Studio 和 CLion
Visual Studio 和 CLion 是兩個流行的整合開發環境(IDE),提供了強大的除錯工具和功能。它們允許您設定斷點、檢查變數和逐步執行程式碼。
# Visual Studio
devenv python
# CLion
clion python
faulthandler
faulthandler
是一個 Python 的內建模組,提供了處理 segmentation fault 的功能。它可以幫助您找出程式碼中的錯誤和問題。
import faulthandler
faulthandler.enable()
編譯Cpython以支援除錯
為了從除錯器中取得有意義的資訊,必須以支援符號除錯的方式編譯Cpython。沒有符號,除錯器在堆積疊追蹤中將無法顯示正確的函式名稱、變數名稱或檔案名稱。
Windows
按照「編譯Cpython」章節中的Windows部分所述,編譯Cpython時請確保選擇了除錯組態,以取得符號除錯資訊:
build.bat -p x64 -c Debug
請注意,在除錯組態下編譯的可執行檔名為`python_d.exe%,這是用於除錯的版本。
macOS或Linux
在「編譯Cpython」章節中,我們建議執行./configure
指令碼並加入--with-pydebug
旗標。如果您尚未這樣做,請傳回並完成這一步。這樣做將會產生正確的可執行檔和符號資訊,以便進行除錯。
LLDB除錯工具
LLDB是macOS上的除錯工具,作為Xcode的一部分,應該已經安裝在您的電腦上。啟動LLDB並載入編譯好的Cpython二進位制檔作為目標:
lldb./python.exe
然後,您可以在LLDB提示符中輸入命令。建立斷點的方法是使用break set
命令,指設定檔案(相對於根目錄)和行號:
(lldb) break set --file Objects/floatobject.c --line 532
或者,您也可以使用更簡短的命令來設定斷點:
(lldb) b Objects/floatobject.c:532
要列出所有當前斷點,可以使用break list
命令:
(lldb) break list
執行Cpython
要執行Cpython,請使用process launch
命令,並附加您通常用於Python的命令列引數。例如,要執行一個簡單的Python命令,可以這樣做:
(lldb) process launch -- -c "print(1)"
如果您要執行一個Python指令碼,可以使用以下命令:
(lldb) process launch -- my_script.py
連線到正在執行的Cpython解譯器
如果您已經有一個正在執行的Cpython解譯器,您可以將LLDB連線到它。只需在LLDB會話中使用process attach
命令,並指定程式ID:
(lldb) process attach --pid 123
您可以在Activity Monitor或使用Python的os.getpid()
函式來找到程式ID。任何在此之前或之後設定的斷點都將暫停程式。
處理斷點
要看到斷點如何工作,請在Objects/floatobject.c
檔中的float_richcompare()
函式上設定一個斷點。然後,執行過程並比較兩個浮點數值,看看我們之前實作的「近似相等」運算子如何工作。
使用LLDB進行Python浮點數比較的除錯
在除錯Python程式時,瞭解浮點數比較的行為至關重要。這篇文章將使用LLDB(Low-Level Debugger)來探索Python浮點數比較的過程。
啟動LLDB
首先,啟動LLDB並執行Python程式。以下命令啟動LLDB並執行Python:
(lldb) process launch -- -c "1.0~=1.1"
這個命令啟動了一個新的Python程式,並執行了1.0~=1.1
的表示式。
LLDB輸出
LLDB輸出了以下資訊:
Process 64421 launched: '/cpython/python.exe' (x86_64)
Process 64421 stopped
* thread #1, queue = '...', stop reason = breakpoint 1.1
frame #0: 0x000000010006a974 python.exe'float_richcompare(v=1.0,
w=1.1, op=6) at floatobject.c:532:26
529 break;
530 case Py_AlE: {
531 double diff = fabs(i - j);
-> 532 const double rel_tol = 1e-9;
533 const double abs_tol = 0.1;
534 r = (((diff <= fabs(rel_tol * j)) ||
這個輸出顯示了LLDB停止在float_richcompare
函式中,這個函式負責比較兩個浮點數。
檢視區域性變數
使用v
命令檢視區域性變數:
(lldb) v
(PyObject *) v = 0x000000010111b370 1.0
(PyObject *) w = 0x000000010111b340 1.1
(int) op = 6
(double) i = 1
(double) j = 1.1000000000000001
(int) r = 0
(double) diff = 0.10000000000000009
(const double) rel_tol = 2.1256294105914498E-314
這個輸出顯示了比較過程中使用的變數,包括兩個浮點數v
和w
、比較運算子op
、暫存變數i
和j
、比較結果r
、差值diff
和相對誤差rel_tol
。
比較過程
根據LLDB的輸出,可以看到比較過程如下:
- 計算兩個浮點數之間的差值
diff
。 - 計算相對誤差
rel_tol
。 - 比較差值
diff
是否小於或等於相對誤差rel_tol
乘以第二個浮點數j
的絕對值。
如果差值diff
小於或等於相對誤差rel_tol
乘以第二個浮點數j
的絕對值,則兩個浮點數被視為相等。
使用LLDB進行除錯
LLDB是一個強大的除錯工具,廣泛用於C/C++應用程式的除錯。下面是使用LLDB進行除錯的步驟:
啟動LLDB
首先,需要啟動LLDB。可以使用以下命令啟動LLDB:
lldb
載入程式
然後,需要載入要除錯的程式。可以使用以下命令載入程式:
lldb./python
設定斷點
設定斷點是除錯的第一步。可以使用以下命令設定斷點:
(lldb) b Objects/floatobject.c:532
這將設定一個斷點在Objects/floatobject.c
檔案的第532行。
執行程式
設定斷點後,需要執行程式。可以使用以下命令執行程式:
(lldb) run
檢視堆積疊資訊
當程式執行到斷點時,LLDB將停止程式的執行並顯示堆積疊資訊。可以使用以下命令檢視堆積疊資訊:
(lldb) bt
這將顯示堆積疊資訊,包括函式呼叫序列和變數值。
使用LLDB指令
LLDB提供了許多指令來幫助除錯。例如,可以使用以下指令檢視變數值:
(lldb) expr (double)fabs(rel_tol)
這將顯示rel_tol
變數的值。
安裝cpython_lldb擴充套件
cpython_lldb是LLDB的一個擴充套件,提供了額外的功能來幫助除錯CPython應用程式。可以使用以下命令安裝cpython_lldb擴充套件:
$ mkdir -p ~/.lldb
$ echo "command script import ~/.lldb/cpython-lldb/cpython_lldb.py" \
>> ~/.lldbinit
$ chmod +x ~/.lldbinit
使用GDB
GDB是另一個流行的除錯工具,可以用於除錯C/C++應用程式。可以使用以下命令啟動GDB:
$ gdb./python
然後,可以使用GDB指令來設定斷點、檢視堆積疊資訊等。
使用GDB和LLDB進行CPython除錯
除錯是軟體開發中一個非常重要的步驟,尤其是在開發像CPython這樣複雜的專案時。GDB和LLDB是兩種常用的除錯工具,下面我們將介紹如何使用它們來除錯CPython。
使用GDB進行除錯
要使用GDB進行除錯,首先需要編譯CPython以包含除錯資訊。然後,可以使用以下命令啟動GDB:
(gdb) run -c "print(1)"
或者,可以使用以下命令啟動GDB並載入一個Python指令碼:
(gdb) run my_script.py
如果你已經有一個正在執行的CPython程式,你可以使用以下命令將GDB附加到該程式:
(gdb) attach 123
其中,123
是程式的ID。
使用LLDB進行除錯
LLDB是另一個常用的除錯工具,尤其是在MacOS上。要使用LLDB進行除錯,首先需要編譯CPython以包含除錯資訊。然後,可以使用以下命令啟動LLDB:
(lldb) run -c "print(1)"
或者,可以使用以下命令啟動LLDB並載入一個Python指令碼:
(lldb) run my_script.py
如果你已經有一個正在執行的CPython程式,你可以使用以下命令將LLDB附加到該程式:
(lldb) attach 123
其中,123
是程式的ID。
設定斷點
在GDB和LLDB中,你可以使用break
命令設定斷點。例如:
(gdb) break PyObject_GetAttr
或者:
(lldb) break PyObject_GetAttr
這將設定一個斷點在PyObject_GetAttr
函式上。
執行和檢查
當程式執行到斷點時,你可以使用next
或step
命令執行程式的一步。例如:
(gdb) next
或者:
(lldb) step
你也可以使用print
命令檢查變數的值。例如:
(gdb) print *(PyLongObject*)v
或者:
(lldb) print *(PyLongObject*)v
Visual Studio偵錯程式
Visual Studio也提供了一個強大的偵錯程式,可以用於除錯CPython。要使用Visual Studio偵錯程式,首先需要建立一個新的專案並新增CPython的原始碼。然後,可以設定斷點並啟動偵錯程式。
CLion偵錯程式
CLion是一個跨平臺的IDE,也提供了一個強大的偵錯程式,可以用於除錯CPython。要使用CLion偵錯程式,首先需要建立一個新的專案並新增CPython的原始碼。然後,可以設定斷點並啟動偵錯程式。
效能最佳化與分析
在修改 CPython 時,瞭解變更對效能的影響至關重要。這章節將探討各種效能分析工具,包括使用 timeit
進行微型效能測試、執行 pyperformance
來比較不同 Python 版本的效能、使用 cProfile
分析執行時間,以及使用追蹤器進行 CPython 執行的效能分析。
使用 timeit 進行微型效能測試
timeit
模組允許您重複執行簡單的 Python 表示式,並計算其執行時間的中位數。這對於比較不同實作的效能非常有用。
$./python -m timeit -n 1000 "x=1; x+=1; x**x"
執行 pyperformance 來比較不同 Python 版本的效能
pyperformance
是一套測試集合,用於比較不同 Python 版本之間的效能差異。
使用 cProfile 分析執行時間
cProfile
是一個內建的 Python 模組,用於分析程式執行時間。它可以幫助您瞭解哪些函式佔用了最多的執行時間。
使用追蹤器進行 CPython 執行的效能分析
追蹤器可以用於分析 CPython 的執行過程,包括函式呼叫和執行時間。
工具清單
工具 | 類別 | 層級 | 作業系統支援 |
---|---|---|---|
timeit | 微型效能測試 | Python | 所有 |
pyperformance | 效能測試 | Python | 所有 |
cProfile | 執行時間分析 | Python | 所有 |
DTrace | 追蹤和分析 | C | Linux, macOS |
注意事項
在進行效能測試之前,請確保關閉所有不必要的應用程式,以確保 CPU 可以完全專注於測試任務。
使用 timeit 進行微型效能測試
您可以使用 timeit
來測試簡單的 Python 表示式。例如:
$./python -m timeit -n 1000 "x=1.0001; y=1.0000; x~=y"
這個命令會重複執行給定的表示式 1000 次,並計算其執行時間的中位數。
實際應用
在本章中,我們修改了 float
型別以支援「約等於」的運算子。您可以使用 timeit
來評估這個比較運算子的效能:
$./python -m timeit -n 1000 "x=1.0001; y=1.0000; x~=y"
結果將顯示這個比較運算子的執行時間中位數。
實作細節
這個比較運算子的實作位於 float_richcompare
函式中,該函式定義在 Objects/floatobject.c
檔案中:
static PyObject*
float_richcompare(PyObject *v, PyObject *w, int op)
{
//...
}
這個函式負責比較兩個浮點數,並傳回比較結果。
從效能評估的視角來看,本文深入探討了Python測試框架、除錯工具以及Cpython效能分析方法。藉由剖析unittest
、pdb
、LLDB、GDB等工具的應用,開發者得以診斷程式碼錯誤、驗證程式碼行為,進而提升程式碼品質。尤其針對Cpython編譯除錯資訊的說明,更能幫助開發者深入理解程式碼底層運作機制。然而,這些工具並非完美無缺,例如LLDB和GDB的學習曲線較陡峭,需要開發者投入額外時間學習。此外,效能分析環節雖然介紹了timeit
、pyperformance
、cProfile
等工具,但仍缺乏更進階的效能分析技巧,例如火焰圖的應用,這對於複雜系統的效能瓶頸分析至關重要。展望未來,預期更多自動化除錯和效能分析工具將問世,結合AI技術的輔助,將大幅降低開發者在程式碼除錯和效能最佳化上的負擔。對於追求高效能的Python開發者,建議深入研究各類別效能分析工具,並結合實際專案經驗,才能將效能調校至最佳狀態。