![](/img/trans.png)
[英]How can I make my python script wait until I tell it it to continue in the terminal?
[英]How can I tell where my python script is hanging?
所以我在調試我的 python 程序時遇到了一個使程序掛起的錯誤,就像處於無限循環中一樣。 現在,我之前遇到了一個無限循環的問題,但是當它掛斷時,我可以殺死程序,python 吐出一個有用的異常,告訴我當我向它發送 kill 命令時程序在哪里終止。 但是,現在,當程序掛斷並按 ctrl-c 時,它不會中止而是繼續運行。 有什么工具可以用來定位掛斷電話嗎? 我是分析的新手,但據我所知,分析器只能為您提供有關已成功完成的程序的信息。 或者您可以使用分析器來調試此類掛斷嗎?
讓我們假設您正在運行您的程序:
python YOURSCRIPT.py
嘗試將您的程序運行為:
python -m trace --trace YOURSCRIPT.py
並且在屏幕上打印很多東西時要有一些耐心。 如果您有一個無限循環,它將永遠持續下去(停止問題)。 如果它卡在某個地方,那么大多數情況下你會卡在 I/O 上或者是一個死鎖。
哇! 已經有 5 個答案,沒有人提出最明顯和最簡單的答案:
print "**010"
、 print "**020"
等一樣基本,貫穿主要區域。我編寫了一個模塊,可以打印出在一個地方掛起時間超過 10 秒的線程。 掛線程.py
跑:
python -m pip install hanging_threads
將此添加到您的代碼中:
from hanging_threads import start_monitoring
start_monitoring(seconds_frozen=10, test_interval=100)
這是一個示例輸出:
-------------------- Thread 5588 --------------------
File "C:\python33\lib\threading.py", line 844, in _exitfunc
t.join()
File "C:\python33\lib\threading.py", line 743, in join
self._block.wait()
File "C:\python33\lib\threading.py", line 184, in wait
waiter.acquire()
當您忘記將另一個線程設置為守護進程時,這會在主線程退出時發生。
如果您的程序太大而復雜,無法使用 pdb 單步執行或使用跟蹤模塊打印每一行,那么您可以嘗試使用我在 8 位游戲編程時代的技巧。 從 Python 2.5 開始,pdb 能夠使用commands
命令將代碼與斷點相關聯。 您可以使用它來打印一條消息並繼續運行:
(Pdb) commands 1
(com) print "*** Breakpoint 1 ***"
(com) continue
(com) end
(Pdb)
當斷點 1 被擊中時,這將打印一條消息並繼續運行。 為其他幾個斷點定義類似的命令。
您可以使用它對您的代碼進行一種二進制搜索。 在代碼中的關鍵位置附加斷點並運行它直到它掛起。 您可以從最后一條消息中知道哪個是它擊中的最后一個斷點。 然后您可以移動其他斷點並重新運行以縮小代碼中它掛起的位置。 沖洗並重復。
順便說一句,在 8 位微型計算機(Commodore 64、Spectrum 等)上,您可以將一個值插入注冊表位置以更改屏幕周圍邊框的顏色。 我曾經設置了幾個斷點來用不同的顏色來做到這一點,所以當程序運行時,它會給出一個迷幻的彩虹顯示,直到它掛起,然后邊框會變成單一顏色,告訴你最后一個斷點是什么。 通過彩虹中每種顏色的數量,您還可以很好地了解不同代碼部分的相對性能。 有時我會懷念這些新式“Windows”機器的簡單性。
從 Python 3.3 開始,有一個內置的faulthandler模塊:
import faulthandler
faulthandler.enable()
您也可以嘗試http://code.activestate.com/recipes/576515-debugging-a-running-python-process-by-interrupting/ 。 只要 Python 進程沒有屏蔽信號,它就應該可以工作,即使 Ctrl-C 不起作用,通常也是這種情況。
如果您的程序太復雜而無法簡單地跟蹤所有函數,您可以嘗試運行它並手動將跟蹤程序(如lptrace)附加到它。 它的工作方式有點像strace
它打印你的程序調用的每個函數。 調用方法如下:
python lptrace -p $STUCK_PROGRAM_PID
請注意,lptrace 需要運行 gdb。
沒有什么比好的舊pdb
import pdb
pdb.run('my_method()',globals(),locals())
然后只需按 (n) 轉到下一個命令, (s) 即可進入。 有關完整參考,請參閱文檔。 按照你的程序一步一步來,你可能會很快弄清楚。
防止這些掛斷比調試它們更容易。
首先: for
循環非常非常難以陷入循環不會終止的情況。 很難。
第二: while
循環相對容易陷入循環。
第一遍是檢查每個while
循環,看它是否必須是一個while
循環。 通常,您可以將while
結構替換for
,並且您將通過重新思考循環來糾正您的問題。
如果您不能用for
替換while
循環,那么您只需證明while
語句中的表達式必須在每次循環中更改。 這並不難證明。
查看循環中的所有條件。 稱其為T 。
查看循環體中的所有邏輯分支。 有沒有辦法在不改變條件T的情況下通過循環?
是的? 那是你的錯誤。 那條邏輯路徑是錯誤的。
不? 太好了,該循環必須終止。
我有一個多線程守護進程,有時會在數小時后卡住,有時會在數周后卡住。 通過調試器運行它是不可行的,甚至可能沒有幫助,因為調試多線程或多進程程序可能很痛苦。 通過跟蹤運行它可能會在它卡住之前填滿千兆字節,如果不是太字節的話。 第二次后台進程出現了掛,我想馬上知道它在哪里,而不需要重新啟動它,加入檢查代碼,通過調試器來運行它,並等待幾小時,幾天或幾周的時間,為的情況下再次掛尚未進行調查。
我被pyrasite救了出來,它允許用戶連接到正在運行的 Python 進程並交互式地檢查幀(受此gist啟發的示例):
$ pyrasite-shell 1071 # 1071 is the Process ID (PID)
Pyrasite Shell 2.0
Connected to '/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/python3.8 /opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/satpy_launcher.py -n localhost /opt/pytroll/pytroll_inst/config/trollflow2.yaml'
Python 3.8.6 | packaged by conda-forge | (default, Dec 26 2020, 05:05:16)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(DistantInteractiveConsole)
>>> import sys
>>> sys._current_frames()
{139652793759488: <frame at 0x7f034b2c9040, file '<console>', line 1, code <module>>, 139653520578368: <frame at 0x7f034b232ac0, file '/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py', line 112, code __init__>}
第一幀沒有信息; 那是我們自己的硫磷礦殼。 然而,第二幀顯示當前我們的腳本卡在第 112 行的pyresample.spherical
模塊中。我們可以使用回溯模塊來獲得完整的回溯:
>>> import traceback
>>> traceback.print_stack(list(sys._current_frames().values())[1])
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/satpy_launcher.py", line 80, in <module>
main()
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/satpy_launcher.py", line 75, in main
run(prod_list, topics=topics, test_message=test_message,
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/launcher.py", line 152, in run
proc.start()
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/process.py", line 121, in start
self._popen = self._Popen(self)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/context.py", line 224, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/context.py", line 277, in _Popen
return Popen(process_obj)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/popen_fork.py", line 19, in __init__
self._launch(process_obj)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/popen_fork.py", line 75, in _launch
code = process_obj._bootstrap(parent_sentinel=child_r)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
self.run()
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/launcher.py", line 268, in process
cwrk.pop('fun')(job, **cwrk)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/plugins/__init__.py", line 403, in covers
cov = get_scene_coverage(platform_name, start_time, end_time,
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/plugins/__init__.py", line 425, in get_scene_coverage
return 100 * overpass.area_coverage(area_def)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollsched/satpass.py", line 242, in area_coverage
inter = self.boundary.contour_poly.intersection(area_boundary)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 494, in intersection
return self._bool_oper(other, -1)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 475, in _bool_oper
inter, edge2 = edge1.get_next_intersection(narcs2, inter)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 326, in get_next_intersection
return None, None
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 298, in intersection
return None
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 264, in intersections
return (SCoordinate(lon, lat),
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 62, in cross2cart
return res
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 112, in __init__
self.cart = np.array(cart)
我們可以使用 Python 內省的所有功能來檢查堆棧,以幫助我們重建卡住的情況。
我自己沒有使用過,但我聽說Eric IDE很好並且有一個很好的調試器。 這也是我所知道的唯一一個帶有 Python 調試器的 IDE
如果您的程序有多個線程,它可能會忽略 ctrl-c,因為一個線程連接到 ctrl-c 處理程序,但實時(失控?)線程對其充耳不聞。 CPython 中的 GIL(全局解釋器鎖)意味着通常任何時候實際上只有一個線程可以運行。 我想我用這個解決了我的(也許)類似的問題
i = 0
for t in threading.enumerate():
if i != 0:# and t.getName() != 'Thread-1':
print t.getName()
t._Thread__stop()
i += 1
一旦你知道線程的名稱; 開始重新執行你的腳本並過濾它們,而不是阻止它們被中止。 i=0 條件防止主線程被中止。
我建議瀏覽並命名所有線程; 如:Thread(target=self.log_sequence_status, name='log status')
這段代碼應該放在啟動失控進程的主程序的末尾
哇 ! 似乎您一次添加了這么多代碼而沒有對其進行測試,以至於您無法說出在程序開始掛起之前添加了哪些代碼......(最可能的問題原因)。
說真的,您應該分步編寫代碼並單獨測試每個步驟(最好是 TDD)。
對於您發現正在運行的 python 代碼和 ctrl-c 不起作用的確切問題,我將嘗試一個原始猜測:您是否使用了一些except:
模糊地捕獲所有異常。 如果您在循環中這樣做(並在管理異常后繼續循環),這很可能是 ctrl-c 不起作用的原因:它被此異常捕獲。 更改為except Exception:
並且不應再捕獲它(還有其他可能 ctrl+c 不像另一個海報建議的線程管理那樣工作,但我相信上述原因更有可能)。
異常鍵盤中斷
Raised when the user hits the interrupt key (normally Control-C or Delete).
在執行期間,會定期檢查中斷。 當內置函數 input() 或 raw_input() 等待輸入時輸入的中斷也會引發此異常。 異常繼承自 BaseException,以免被捕獲 Exception 的代碼意外捕獲,從而阻止解釋器退出。
Changed in version 2.5: Changed to inherit from BaseException.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.