您可以使用--format
參數更改檔案格式以產生 speedscope 設定檔或原始資料。有關其他選項的信息,請參閱py-spy record --help
,包括更改採樣率、過濾為僅包含持有 GIL 的線程、分析本機 C 擴展、顯示線程 ID、分析子進程等。
Top 顯示了 Python 程式中哪些函數佔用最多時間的即時視圖,類似於 Unix top 指令。運行 py-spy:
py-spy top --pid 12345
# OR
py-spy top -- python myprogram.py
將顯示 python 程式的即時更新進階視圖:
py-spy 也可以使用dump
指令顯示每個 python 執行緒的目前呼叫堆疊:
py-spy dump --pid 12345
這會將每個執行緒的呼叫堆疊以及其他一些基本進程資訊轉儲到控制台:
這對於您只需要一個呼叫堆疊來確定您的 python 程式掛在哪裡的情況很有用。該命令還能夠透過設定--locals
標誌來列印與每個堆疊框架關聯的局部變數。
該專案旨在讓您分析和調試任何正在運行的 Python 程序,即使該程式正在服務生產流量。
雖然還有許多其他 python 分析項目,但幾乎所有項目都需要以某種方式修改分析程序。通常,分析程式碼在目標 python 進程內部運行,這會減慢並改變程式的運行方式。這意味著使用這些分析器來調試生產服務中的問題通常並不安全,因為它們通常會對效能產生明顯的影響。
py-spy 的工作原理是使用 Linux 上的 process_vm_readv 系統呼叫、OSX 上的 vm_read 呼叫或 Windows 上的 ReadProcessMemory 呼叫直接讀取 python 程式的記憶體。
透過查看全域 PyInterpreterState 變數來取得解釋器中執行的所有 Python 線程,然後迭代每個線程中的每個 PyFrameObject 以取得呼叫堆疊,可以計算出 Python 程式的呼叫堆疊。由於Python ABI在版本之間發生變化,我們使用rust的bindgen為我們關心的每個Python解釋器類別產生不同的rust結構,並使用這些產生的結構來確定Python程式中的記憶體佈局。
由於位址空間佈局隨機化,獲取 Python 解釋器的記憶體位址可能有點棘手。如果目標 python 解釋器附帶了符號,那麼透過根據 Python 版本取消引用interp_head
或_PyRuntime
變量,可以輕鬆找出解釋器的記憶體位址。然而,許多 Python 版本要么附帶了剝離的二進位文件,要么附帶了 Windows 上相應的 PDB 符號文件。在這些情況下,我們掃描 BSS 部分,尋找看起來可能指向有效 PyInterpreterState 的位址,並檢查該位址的佈局是否符合我們的預期。
是的! py-spy 支援在 x86_64 Linux 和 Windows 上分析用 C/C++ 或 Cython 等語言編寫的本機 python 擴充功能。您可以透過在命令列上傳遞--native
來啟用此模式。為了獲得最佳結果,您應該使用符號編譯 Python 擴充。對於 Cython 程式也值得注意的是 py-spy 需要產生的 C 或 C++ 檔案才能傳回原始 .pyx 檔案的行號。閱讀部落格文章以了解更多資訊。
透過將--subprocesses
標誌傳遞到記錄或頂視圖,py-spy 還將包含任何作為目標程式的子程序的 python 程序的輸出。這對於分析使用多處理或gunicorn工作池的應用程式非常有用。 py-spy 將監視正在建立的新進程,並自動附加到它們並在輸出中包含來自它們的樣本。記錄視圖將包括呼叫堆疊中每個程式的 PID 和命令列,子進程顯示為其父進程的子進程。
py-spy 的工作原理是從不同的 python 進程讀取內存,出於安全原因,這可能是不允許的,具體取決於您的作業系統和系統設定。在許多情況下,以 root 使用者身分執行(使用 sudo 或類似命令)可以繞過這些安全限制。 OSX 始終需要以 root 身份運行,但在 Linux 上,這取決於您啟動 py-spy 的方式以及系統安全設定。
在 Linux 上,預設配置是在附加到非子進程時需要 root 權限。對於 py-spy,這意味著您可以透過讓 py-spy 建立進程( py-spy record -- python myprogram.py
)來進行分析,無需root 存取權限,但透過指定PID 附加到現有進程通常需要root ( sudo py-spy record --pid 123456
)。您可以透過設定 ptrace_scope sysctl 變數在 Linux 上刪除此限制。
py-spy 嘗試僅包含正在運行程式碼的執行緒的堆疊跟踪,並排除正在睡眠或空閒的執行緒。如果可能,py-spy 會嘗試從作業系統獲取該線程活動資訊:在 Linux 上讀取/proc/PID/stat
,在 OSX 上使用 mach thread_basic_info 調用,以及查看當前 SysCall 是否已知處於空閒狀態在Windows 上。
這種方法有一些限制,但可能會導致空閒線程仍然被標記為活動線程。首先,我們必須在暫停程序之前獲取該線程活動信息,因為從暫停的程序中獲取該信息將導致它始終返回該線程處於空閒狀態。這意味著存在潛在的競爭條件,我們獲取線程活動,然後當我們獲取堆疊追蹤時線程處於不同的狀態。對於 Linux 上的 FreeBSD 和 i686/ARM 處理器,查詢作業系統的執行緒活動也尚未實作。在 Windows 上,IO 上阻塞的呼叫也不會被標記為空閒,例如從 stdin 讀取輸入時。最後,在某些 Linux 上,我們使用的 ptrace Attach 呼叫可能會導致空閒執行緒暫時喚醒,從而在從 procfs 讀取時導致誤報。由於這些原因,我們還有一個啟發式回退,將 python 中已知的某些已知呼叫標記為空閒。
您可以透過設定--idle
標誌來停用此功能,該標誌將包括 py-spy 認為空閒的幀。
我們透過查看 Python 3.6 及更早版本的_PyThreadState_Current
符號指向的 threadid 值以及從 Python 3.7 及更高版本中的_PyRuntime
結構中找出等效值來取得 GIL 活動。這些符號可能不包含在您的 python 發行版中,這將導致解析哪個執行緒保留 GIL 失敗。目前 GIL 使用情況也在top
視圖中顯示為 %GIL。
傳遞--gil
標誌將僅包含持有全域解釋器鎖的執行緒的追蹤。在某些情況下,這可能是您的 python 程式如何花費時間的更準確的視圖,儘管您應該意識到這會錯過在仍處於活動狀態時釋放 GIL 的擴充功能中的活動。
OSX 有一個稱為系統完整性保護的功能,甚至可以防止 root 使用者從 /usr/bin 中的任何二進位檔案讀取記憶體。不幸的是,這包括 OSX 附帶的 python 解釋器。
有幾種不同的方法可以處理這個問題:
即使以 root 身分執行,在 docker 容器內執行 py-spy 通常也會出現權限被拒絕的錯誤。
這個錯誤是由於docker限制了我們正在使用的process_vm_readv系統呼叫而造成的。這可以透過在啟動 docker 容器時設定--cap-add SYS_PTRACE
來覆蓋。
或者,您可以編輯 docker-compose yaml 文件
your_service:
cap_add:
- SYS_PTRACE
請注意,您需要重新啟動 docker 容器才能使此設定生效。
您也可以使用主機作業系統中的 py-spy 來分析 docker 容器內執行的正在執行的進程。
py-spy 需要SYS_PTRACE
才能讀取進程記憶體。 Kubernetes 預設會放棄該功能,導致錯誤
Permission Denied: Try running again with elevated permissions by going 'sudo env "PATH=$PATH" !!'
處理此問題的建議方法是編輯規格並添加該功能。對於部署,這是透過將其新增至Deployment.spec.template.spec.containers
來完成的
securityContext:
capabilities:
add:
- SYS_PTRACE
有關此內容的更多詳細信息,請參見:https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capability-for-a-container 請注意,這將刪除現有的pod 並再次建立它們。
Alpine python 選擇退出manylinux
輪子:pypa/pip#3969(評論)。您可以覆寫此行為,以使用 pip 在 Alpine 上安裝 py-spy,方法是:
echo 'manylinux1_compatible = True' > /usr/local/lib/python3.7/site-packages/_manylinux.py
或者,您可以從 GitHub 發布頁面下載 musl 二進位。
透過設定--nonblocking
選項,py-spy 不會暫停您正在分析的目標 python。雖然使用 py-spy 從進程中採樣對效能的影響通常極低,但設定此選項將完全避免中斷正在運行的 python 程式。
設定此選項後,py-spy 將在 python 進程運行時讀取解釋器狀態。由於我們用來讀取記憶體的呼叫不是原子的,並且我們必須發出多個呼叫來獲取堆疊跟踪,這意味著我們在採樣時偶爾會遇到錯誤。這可能表現為採樣時錯誤率增加,或表現為輸出包含部分堆疊幀。
還沒有=)。
如果您希望在 py-spy 中看到某些功能,請按讚相應的問題或建立一個新問題來描述缺少的功能。
py-spy 遵循 CLICOLOR 規範,因此在您的環境中設定CLICOLOR_FORCE=1
將使 py-spy 列印彩色輸出,即使透過管道傳輸到尋呼機也是如此。
py-spy 很大程度上受到 Julia Evans 在 rbspy 上的出色工作的啟發。特別是,產生 Flamegraph 和 speedscope 檔案的程式碼直接取自 rbspy,而專案使用從 rbspy 衍生出來的 read-process-memory 和 proc-maps 套件。
py-spy 是在 MIT 許可證下發布的,請參閱許可證文件以獲取全文。