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

這個輸出顯示了比較過程中使用的變數,包括兩個浮點數vw、比較運算子op、暫存變數ij、比較結果r、差值diff和相對誤差rel_tol

比較過程

根據LLDB的輸出,可以看到比較過程如下:

  1. 計算兩個浮點數之間的差值diff
  2. 計算相對誤差rel_tol
  3. 比較差值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函式上。

執行和檢查

當程式執行到斷點時,你可以使用nextstep命令執行程式的一步。例如:

(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追蹤和分析CLinux, 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效能分析方法。藉由剖析unittestpdb、LLDB、GDB等工具的應用,開發者得以診斷程式碼錯誤、驗證程式碼行為,進而提升程式碼品質。尤其針對Cpython編譯除錯資訊的說明,更能幫助開發者深入理解程式碼底層運作機制。然而,這些工具並非完美無缺,例如LLDB和GDB的學習曲線較陡峭,需要開發者投入額外時間學習。此外,效能分析環節雖然介紹了timeitpyperformancecProfile等工具,但仍缺乏更進階的效能分析技巧,例如火焰圖的應用,這對於複雜系統的效能瓶頸分析至關重要。展望未來,預期更多自動化除錯和效能分析工具將問世,結合AI技術的輔助,將大幅降低開發者在程式碼除錯和效能最佳化上的負擔。對於追求高效能的Python開發者,建議深入研究各類別效能分析工具,並結合實際專案經驗,才能將效能調校至最佳狀態。