1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Python 程序調(diào)試分析大殺器合集

        共 5444字,需瀏覽 11分鐘

         ·

        2022-05-28 16:49

        問題背景

        在之前的?debug 系列文章[1]?里,我們曾經(jīng)介紹過一個 Python 進程 hang 住的問題的排查。最近正好又碰到了類似的問題,來簡單調(diào)研總結(jié)一下處理這類問題的排查處理方法,以及各種 Python 調(diào)試,性能分析中可以使用的強大工具。

        思路

        對于程序 hang 住,但 CPU 等使用率較低的情況,最常用的做法是獲取程序當(dāng)前的 callstack,查看是否在“等待”什么東西。理想情況下,我們希望能直接得到 Python 程序當(dāng)前在執(zhí)行哪一行代碼,定位根源。

        對于程序跑得慢,且 CPU 使用率相對正?;虮容^高,說明程序還是在“干活”的。這時候一般會采用各種 profiling 工具,去多次“采樣” callstack,查看程序在各行代碼上的所耗費時間的百分比,以此來定位性能瓶頸。

        可見獲取 callstack 是一個共性需求,我們接下來會以此展開,來看看各種處理方法。

        常規(guī)方法

        先介紹一下不需要額外庫的排查方法,也是很多文章中會提到的常見做法。

        如果可以改代碼

        如果這個 hang 住的程序是我們自己控制的,比如自己開發(fā)的項目,線下的測試環(huán)境可以修改重啟等,那么我們可以通過植入“信號 handler”的方法來獲取到程序的 callstack,進而定位到底 hang 在哪一行代碼執(zhí)行上。具體代碼如下:

        import?code,?traceback,?signal

        def?debug(sig,?frame):
        ????"""Interrupt?running?process,?and?provide?a?python?prompt?for
        ????interactive?debugging."""

        ????d={'_frame':frame}?????????#?Allow?access?to?frame?object.
        ????d.update(frame.f_globals)??#?Unless?shadowed?by?global
        ????d.update(frame.f_locals)

        ????i?=?code.InteractiveConsole(d)
        ????message??=?"Signal?received?:?entering?python?shell.\nTraceback:\n"
        ????message?+=?''.join(traceback.format_stack(frame))
        ????i.interact(message)

        def?listen():
        ????signal.signal(signal.SIGUSR1,?debug)??#?Register?handler

        然后只要在程序開始執(zhí)行前的代碼里先調(diào)用一下listen(),后面當(dāng)程序卡住時,可以通過另一個 Python 進程來發(fā)送信號:

        os.kill(pid,?signal.SIGUSR1)

        然后就能得到當(dāng)時的 callstack 了。不過這個方法只能在 Linux/Unix 系的操作系統(tǒng)上使用。

        如果不能改代碼

        很多情況下,我們并不能修改相應(yīng)的 Python 程序,比如是在線上運行的程序,不能隨意關(guān)掉;或者并不是自己開發(fā)的項目(雖然 Python 庫一般來說都是代碼開放的)。這時候我們可以使用一些 debug 通用進程的方式來進行排查,例如:

        • 可以用pstackgdb來打印 native callstack。
        • strace來做進程調(diào)用的追蹤。
        • 使用perf等動態(tài)追蹤工具來進行 profiling。

        同樣的,這些方法也基本只能在 Linux 系統(tǒng)上使用,且拿到的信息大多數(shù)都是系統(tǒng)調(diào)用,不容易直接映射到對于的 Python 代碼上。

        利用專有工具

        Java 中自帶的 JDK 里有很多強大的開發(fā)者工具,可以幫助我們來對 Java 程序做各類排查診斷,另外也衍生出了包括?arthas[2]?這樣優(yōu)秀的第三方工具??上?Python 原生并沒有如此完善的工具體系,但好在類似的第三方工具還是有不少的,我們這就來嘗試一下。

        示例代碼

        首先我們先來“人造”一個非常簡單的程序 hang 住的代碼:

        import?time

        def?func1():
        ????print('hello')
        ????func2()
        ????print('bye')

        def?func2():
        ????print('enter?func2')
        ????time.sleep(100)
        ????print('leave?func2')

        def?main():
        ????func1()

        if?__name__?==?'__main__':
        ????main()

        執(zhí)行這個程序,程序會在sleep處 hang 住,然后可以用ps命令獲取到相應(yīng)的 pid。后續(xù)我們希望各類工具都能直接使用這個 pid,而不需要對程序本身進行修改或重啟操作,就能獲取到相關(guān)的 callstack 或更豐富的 profiling 信息。

        大家如果對性能 profiling 的排查感興趣,也可以自己構(gòu)造一個消耗較高 CPU 資源的程序(例如啟動 2 個子進程來計算圓周率)來進行后面的各種實驗。

        系統(tǒng)工具

        我們先用gdb來看下獲取的 callstack 長啥樣,執(zhí)行:

        gdb?attach?

        然后通過bt命令打印出 callstack:

        gdb 獲取的 callstack

        可見 native 的 callstack 包含的內(nèi)容比較“細節(jié)”,雖然大致可以看出程序是 hang 在了 sleep 操作上,但 Python 內(nèi)部代碼的調(diào)用路徑基本是不可見的。

        pystack-debugger

        找這個項目的出發(fā)點是想看看能否在gdb里支持 Python 調(diào)用 frame 的信息獲取。其它網(wǎng)站有看到過一些 gdb 的宏的介紹,不過安裝配置比較麻煩,還是這個工具用起來最簡單直接。

        項目地址:pystack[3]

        安裝:

        pip?install?pystack-debugger

        執(zhí)行:

        pystack?

        返回信息:

        ??pystack?3570
        Dumping?Threads....

        ??File?"hang.py",?line?17,?in?
        ????main()
        ??File?"hang.py",?line?14,?in?main
        ????func1()
        ??File?"hang.py",?line?5,?in?func1
        ????func2()
        ??File?"hang.py",?line?10,?in?func2
        ????time.sleep(100)
        ??File?"",?line?1,?in?

        Wow,效果很好,直接就找到了我們 hang 在了 sleep 這一行上。

        hypno

        項目地址:hypno[4]

        安裝同樣用 pip:

        pip?install?hypno

        這個庫的目標(biāo)不太一樣,主要是為了 inject 到正在運行的 Python 進程里跑一些代碼,所以靈活性比單純打 callstack 更高,比如可以打印一些變量之類。我們用如下的命令來實現(xiàn)獲取 callstack 的功能:

        hypno??"import?traceback;?traceback.print_stack()"

        可以看到原來的進程中打印出了我們所需要的信息:

        ??python?hang.py
        hello
        enter?func2
        ??File?"hang.py",?line?17,?in?
        ????main()
        ??File?"hang.py",?line?14,?in?main
        ????func1()
        ??File?"hang.py",?line?5,?in?func1
        ????func2()
        ??File?"hang.py",?line?10,?in?func2
        ????time.sleep(100)
        ??File?"",?line?1,?in?
        leave?func2
        bye

        這里也能直接定位到進程當(dāng)時運行到的位置,而且“入侵”完成后還不影響原先程序的運行,會一直跑完正常退出。我琢磨著這個工具應(yīng)該還可以做不少其它的騷操作……

        py-spy

        在搜尋 profiling 相關(guān)工具時看到了這個項目,感覺非常強大。

        項目地址:py-spy[5]

        安裝:

        pip?install?py-spy

        獲取 callstack 可以使用 dump 命令:

        ??py-spy?dump?--pid?5103
        Process?5103:?python?hang.py
        Python?v3.8.8?(/home/zijie0/miniconda3/envs/playground/bin/python3.8)

        Thread?5103?(idle)
        ????func2?(hang.py:10)
        ????func1?(hang.py:5)
        ????main?(hang.py:14)
        ?????(hang.py:17)

        此外py-spy還支持top形式的實時 profiling,以及生成火焰圖(操作稍麻煩)的功能,除了程序 hang 之外也能做性能優(yōu)化的監(jiān)控工具。不過易用性上來說比下面要介紹的austin還是稍微差了些。

        py-spy top 模式

        在我的電腦上看這個 sample 百分比的記錄好像也有些問題。

        austin

        本以為py-spy已經(jīng)足夠強大了,沒想到又找到了這個無敵的austin!

        項目地址:austin[6]austin-tui[7]

        安裝:

        conda?install?-c?conda-forge?austin-tui

        這是一個集大成的 profiling 工具,不光可以看 callstack,還可以看對應(yīng)的采樣(近似理解為程序時間開銷)百分占比信息,以及實時更新的 flamegraph 等,竟然還是跨平臺的!官網(wǎng)上還有更多高級特性,感覺已經(jīng)可以跟 JDK 里的 visualvm 來媲美了!這個例子里的 callstack 比較簡單,生成出來長這樣:

        austin-tui

        官網(wǎng)上給的樹狀圖和 flamegraph 對于復(fù)雜程序的性能問題排查會很有幫助:

        austin tree view
        austin 火焰圖

        作者還很貼心的提供了 cheatsheet 來幫助你更好的使用這個工具,以后可以在項目里多嘗試一下了。

        austin cheatsheet

        值得一提的是在調(diào)研跟austin類似的工具框架時,還找到了這個?pyroscope[8],把 profiling 做成了個平臺(也支持 Python),還成立了家公司進入了 YC 的孵化器,未來說不定也是個不錯的細分市場呀。

        其它項目

        搜了一圈下來發(fā)現(xiàn) Python 這方面的工具遠比我想象中的要豐富,還有很多相對冷門或者有些時間沒有維護的項目,這里也列舉出來供大家參考:

        • pyinstrument[9]
        • pystuck[10]
        • PyFlame[11]
        • pyrasite[12]
        • pydbattach[13]

        大家在平時工作中有找到過什么好用的 Python 開發(fā)者工具嗎?也歡迎在評論區(qū)留言討論 :)

        感謝收看,我們下期再見。

        參考資料

        [1]

        debug 系列文章:?https://zhuanlan.zhihu.com/p/385962965

        [2]

        arthas:?https://github.com/alibaba/arthas

        [3]

        pystack:?https://github.com/wooparadog/pystack

        [4]

        hypno:?https://github.com/kmaork/hypno

        [5]

        py-spy:?https://github.com/benfred/py-spy

        [6]

        austin:?https://github.com/P403n1x87/austin

        [7]

        austin-tui:?https://github.com/p403n1x87/austin-tui

        [8]

        pyroscope:?https://github.com/pyroscope-io/pyroscope

        [9]

        pyinstrument:?https://github.com/joerick/pyinstrument

        [10]

        pystuck:?https://github.com/alonho/pystuck

        [11]

        PyFlame:?https://github.com/uber-archive/pyflame

        [12]

        pyrasite:?https://github.com/lmacken/pyrasite

        [13]

        pydbattach:?https://github.com/albertz/pydbattach

        Python貓技術(shù)交流群開放啦!群里既有國內(nèi)一二線大廠在職員工,也有國內(nèi)外高校在讀學(xué)生,既有十多年碼齡的編程老鳥,也有中小學(xué)剛剛?cè)腴T的新人,學(xué)習(xí)氛圍良好!想入群的同學(xué),請在公號內(nèi)回復(fù)『交流群』,獲取貓哥的微信(謝絕廣告黨,非誠勿擾?。?/span>~


        還不過癮?試試它們




        微軟發(fā)布 Python 的 JIT 編譯器:Pyjion

        Python 函數(shù)為什么會默認(rèn)返回 None?

        警惕!Python 中少為人知的 10 個安全陷阱!

        Python調(diào)試神器:PySnooper詳細使用指南!

        Python 中最常用的 5 種線程鎖你會用嗎?

        Python 設(shè)計和歷史的 27 個問題


        如果你覺得本文有幫助
        請慷慨分享點贊,感謝啦
        瀏覽 139
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产高清一区在线观看 | 女女同の激しい爱与 | 啊cao翻你个小sao货视频 | 宝贝啊用力嗯轻一点小说 | 国产美女啪啪视频 | 四虎精品寂寞少妇在线观看 | 丰满少妇高潮久久久久久 | 两根撑满好大好爽太满了3p | 中文字幕成人乱码熟女 | 欧美日韩在线观看一区二区三区 |