Python 程序的抽样分析器 - Py-Spy
?
From:https://python.freelycode.com/contribution/detail/1320
GitHub 地址:https://github.com/benfred/py-spy
?
?
Py-Spy 是 Python 程序的抽樣分析器。 它允許您可視化 Python 程序正花費(fèi)時間在哪部分,而無需重新啟動程序或以任何方式修改代碼。 Py-Spy 的開銷極低:它使用 Rust 語言編寫,速度快,不會在與配置的 Python 程序相同的進(jìn)程中運(yùn)行,也不會以任何方式中斷正在運(yùn)行的程序。 這意味著 Py-Spy 可以安全地用于生產(chǎn)環(huán)境的 Python 代碼。
Py-Spy 適用于Linux,OSX 和 Windows,并支持分析所有最新版本的 CPython 解釋器(版本2.3-2.7和3.3-3.7)
?
安裝
可以從 PyPI 安裝預(yù)構(gòu)建的二進(jìn)制 wheel 格式文件:pip install py-spy
?
用法
py-spy 在命令行中工作,并獲取要從中取樣的程序的 PID 或要運(yùn)行的 python 程序的命令行:
默認(rèn)的可視化是python程序的類似top命令輸出的實(shí)時視圖:
?
還支持從運(yùn)行過程生成火焰圖:
這將生成一個SVG文件,如下所示:
通過將 --dump 傳遞給命令行,還可以為每個線程轉(zhuǎn)儲當(dāng)前的調(diào)用堆棧。
?
經(jīng)常問的問題
為什么我們需要另一個Python分析器?
該項(xiàng)目旨在讓您分析和調(diào)試任何正在運(yùn)行的Python程序,即使該程序正在為生產(chǎn)流量提供服務(wù)。
雖然還有許多其他python分析項(xiàng)目,但幾乎所有項(xiàng)目都需要以某種方式修改被分析的程序。 通常,分析代碼在目標(biāo)python進(jìn)程內(nèi)部運(yùn)行,這將減慢并改變程序的運(yùn)行方式。 這意味著使用這些分析器來調(diào)試生產(chǎn)服務(wù)中的問題通常不安全,因?yàn)樗鼈兺ǔπ阅墚a(chǎn)生顯著影響。 唯一一個完全在單獨(dú)進(jìn)程中運(yùn)行的Python探查器是pyflame,它通過使用ptrace系統(tǒng)調(diào)用來描述遠(yuǎn)程python進(jìn)程。 雖然pyflame是一個很棒的項(xiàng)目,但它還不支持Python 3.7,并且不適用于OSX或Windows。
py-spy如何運(yùn)作?
Py-spy通過使用Linux上的process_vm_readv系統(tǒng)調(diào)用,OSX上的vm_read調(diào)用或Windows上的ReadProcessMemory調(diào)用直接讀取python程序的內(nèi)存。
通過查看全局PyInterpreterState變量來獲取Python程序的調(diào)用堆棧,以獲取在解釋器中運(yùn)行的所有Python線程,然后迭代每個線程中的每個PyFrameObject以獲取調(diào)用堆棧。 由于Python ABI在不同版本之間發(fā)生變化,我們使用rusts的bindgen為我們關(guān)心的每個Python interperator類生成不同的rust結(jié)構(gòu),并使用這些生成的結(jié)構(gòu)來計(jì)算Python程序中的內(nèi)存布局。
由于地址空間布局隨機(jī)化,獲取Python解釋器的內(nèi)存地址可能有點(diǎn)棘手。 如果目標(biāo)python解釋器帶有符號,則通過取消引用interp_head或_PyRuntime變量(取決于Python版本),很容易找出解釋器的內(nèi)存地址。 但是,許多Python版本附帶了剝離的二進(jìn)制文件,或者在Windows上沒有相應(yīng)的PDB符號文件。 在這些情況下,我們通過BSS部分掃描看起來像是指向有效PyInterpreterState的地址,并檢查該地址的布局是否符合我們的預(yù)期。
py-spy配置文件原生擴(kuò)展?
由于我們通過查看PyInterpreterState來獲取python程序的調(diào)用堆棧,我們還沒有獲得有關(guān)非python線程的信息,也無法分析像Cython或C ++等語言編寫的本機(jī)擴(kuò)展。 本機(jī)代碼將顯示為在調(diào)用本機(jī)函數(shù)的Python行中花費(fèi)時間,而不是現(xiàn)在它自己的條目。
應(yīng)該可以使用libunwind之類的東西來分析Python Extensions中的原生代碼。 如果這是你感興趣的事情,請?zhí)岢鲞@個問題。
你什么時候需要以sudo身份運(yùn)行?
Py-spy通過從不同的python進(jìn)程讀取內(nèi)存來工作,出于安全原因,這可能不允許,具體取決于您的操作系統(tǒng)和系統(tǒng)設(shè)置。 在許多情況下,以root用戶(使用sudo或類似用戶)運(yùn)行可以解決這些安全限制。 OSX總是需要以root身份運(yùn)行,但在Linux上它取決于你如何啟動py-spy和系統(tǒng)安全設(shè)置。
在Linux上,默認(rèn)配置是在附加到非子進(jìn)程時需要root權(quán)限。 對于py-spy,這意味著您可以通過使用py-spy來創(chuàng)建進(jìn)程(py-spy -- python myprogram.py)從而不需要root權(quán)限來分析,但通過指定PID附加到現(xiàn)有進(jìn)程通常需要root(sudo py-spy -pid 123456)。 您可以通過設(shè)置ptrace_scope sysctl變量來消除linux對此的限制。
?
在Kubernetes下運(yùn)行
py-spy需要SYS_PTRACE才能讀取進(jìn)程內(nèi)存。 Kubernetes默認(rèn)情況下會丟棄該功能,從而導(dǎo)致錯誤
處理此問題的推薦方法是編輯規(guī)范和所有功能。 對于部署,可以通過將此添加到Deployment.spec.template.spec.containers來完成
有關(guān)詳細(xì)信息,請?jiān)L問:https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capabilities-for-a-container
請注意,這將刪除現(xiàn)有的pod并再次創(chuàng)建。
為什么我在OSX上分析/usr/bin/python時遇到問題?
OSX有一個稱為系統(tǒng)完整性保護(hù)的功能,即使root用戶也無法從位于/usr/bin中的任何二進(jìn)制文件中讀取內(nèi)存。 不幸的是,這包括了OSX附帶的python解釋器。
有幾種不同的方法可以解決這個問題:
你可以安裝一個不同的Python發(fā)行版(你可能想要遠(yuǎn)離python2遷移=)
你可以使用virtualenv在SIP不適用的環(huán)境中運(yùn)行系統(tǒng)python。
你可以禁用系統(tǒng)完整性保護(hù)。
你是如何通過PyPI分發(fā)Rust可執(zhí)行二進(jìn)制文件的?
好吧,沒有人真正問過我這個 - 但我想分享,因?yàn)檫@是一個非常可怕的黑科技,可能對其他人有用。
我真的想通過PyPI分發(fā)這個軟件包,因?yàn)槭褂胮ip進(jìn)行安裝會使大多數(shù)Python程序員更容易安裝到他們的系統(tǒng)上。 不幸的是,將可執(zhí)行文件安裝為python腳本并不是setuptools支持的。
為了解決這個問題,我使用setuptools_rust包來構(gòu)建py-spy二進(jìn)制文件,然后重寫distutils install命令將構(gòu)建的二進(jìn)制文件復(fù)制到python腳本文件夾中。 通過為所支持的平臺預(yù)先構(gòu)建的輪子,這意味著我們可以使用pip安裝py-spy,而不需要在安裝它的機(jī)器上安裝Rust編譯器。
這是否能在BSD上運(yùn)行? 支持32位Windows? 與PyPy集成? 使用USC-16版本的Python2?
還沒有=)。
可信度
py-spy受到Julia Evans在rbspy上的出色工作的啟發(fā)。 特別是,生成火焰圖的代碼直接來自rbspy,這個項(xiàng)目使用從rbspy中分離出來的(read-process-memory和proc-maps)包。
許可證
Py-spy是在GNU通用公共許可證v3.0下發(fā)布的,請參閱LICENSE文件以獲取全文。
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的Python 程序的抽样分析器 - Py-Spy的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FRIDA - API使用篇:rpc、P
- 下一篇: 小甲鱼 OllyDbg 教程系列 (三)