実行中の Python アプリケーションのスタック トレースを表示する
-
02-07-2019 - |
質問
私はこの Python アプリケーションを持っていますが、時々スタックしてしまい、どこで動作するのかわかりません。
Python インタープリターに信号を送り、実行中の正確なコードを表示する方法はありますか?
オンザフライスタックトレースのようなものでしょうか?
関連する質問:
解決
このような状況で使用するモジュールがあります。プロセスが長時間実行されるが、不明で再現不可能な理由で時々停止する場合です。これは少しハック的で、UNIX 上でのみ動作します (シグナルが必要です)。
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
使用するには、プログラムの起動時に listen() 関数を呼び出して (site.py に貼り付けて、すべての Python プログラムで使用させることもできます)、実行させます。任意の時点で、kill を使用するか、Python でプロセスに SIGUSR1 シグナルを送信します。
os.kill(pid, signal.SIGUSR1)
これにより、プログラムが現在の時点で Python コンソールに中断され、スタック トレースが表示され、変数を操作できるようになります。実行を続行するには、control-d (EOF) を使用します (ただし、信号を送信した時点で I/O などが中断される可能性があるため、完全に非侵入的ではないことに注意してください。
同じことを行う別のスクリプトがありますが、パイプを介して実行中のプロセスと通信する点が異なります(バックグラウンドプロセスのデバッグなどを可能にするため)。ここに投稿するには少し量が多いですが、追加しました。 Python クックブックのレシピ.
他のヒント
シグナルハンドラーをインストールするという提案は良い提案であり、私はそれをよく使用しています。例えば、 ビズル デフォルトでは、以下を呼び出す SIGQUIT ハンドラーがインストールされます。 pdb.set_trace()
すぐにあなたを pdb プロンプト。(「 bzrlib.breakin 正確な詳細についてはモジュールのソースを参照してください。) pdb を使用すると、現在のスタック トレースを取得できるだけでなく、変数などを検査することもできます。
ただし、シグナル ハンドラーをインストールする予見がなかったプロセスをデバッグする必要がある場合があります。Linux では、gdb をプロセスにアタッチし、いくつかの gdb マクロを使用して Python スタック トレースを取得できます。置く http://svn.python.org/projects/python/trunk/Misc/gdbinit で ~/.gdbinit
, 、 それから:
- gdb をアタッチします。
gdb -p
PID
- Python スタック トレースを取得します。
pystack
残念ながら完全に信頼できるわけではありませんが、ほとんどの場合に機能します。
最後に添付します strace
多くの場合、プロセスが何を行っているかについての良いアイデアが得られます。
私はほとんど常に複数のスレッドを扱っており、メインスレッドは一般的にあまり処理を行っていないため、最も興味深いのはすべてのスタックをダンプすることです (これは Java のダンプに似ています)。これはに基づいた実装です このブログ:
import threading, sys, traceback
def dumpstacks(signal, frame):
id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
code = []
for threadId, stack in sys._current_frames().items():
code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId))
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
if line:
code.append(" %s" % (line.strip()))
print "\n".join(code)
import signal
signal.signal(signal.SIGQUIT, dumpstacks)
のスタック トレースの取得 準備ができていない Python プログラム、ストック Python で実行 デバッグシンボルなし でできる パイラサイト. 。Ubuntu Trusty では私にとって魅力的に機能しました。
$ sudo pip install pyrasite
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
$ sudo pyrasite 16262 dump_stacks.py # dumps stacks to stdout/stderr of the python program
(@Albert に感謝します。その答えには、他のツールの中でもとりわけ、これへのポインタが含まれていました。)
>>> import traceback
>>> def x():
>>> print traceback.extract_stack()
>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]
スタック トレースを適切にフォーマットすることもできます。 ドキュメント.
編集:@Douglas Leeder が提案したように、Java の動作をシミュレートするには、これを追加します。
import signal
import traceback
signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))
アプリケーションのスタートアップ コードに追加します。次に、送信してスタックを印刷できます SIGUSR1
実行中の Python プロセスに送信します。
の トレースバック モジュールにはいくつかの優れた関数があります。その中には次のようなものがあります。印刷スタック:
import traceback
traceback.print_stack()
試してみることができます フォールトハンドラーモジュール. 。を使用してインストールします pip install faulthandler
そして次を追加します:
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
プログラムの最初に。次に、SIGUSR1 をプロセスに送信します (例: kill -USR1 42
) すべてのスレッドの Python トレースバックを標準出力に表示します。 ドキュメントを読む その他のオプションについては (例:ファイルにログインする) などの方法でトレースバックを表示します。
このモジュールは Python 3.3 の一部になりました。Python 2 については、次を参照してください。 http://faulthandler.readthedocs.org/
ここで本当に役に立ったのは、 スピブのヒント (評判ポイントがあれば賛成票を投じてコメントするだろう) 準備ができていない Python プロセス。私がやるまでうまくいかなかったことを除いて gdbinit スクリプトを変更しました. 。それで:
ダウンロード http://svn.python.org/projects/python/trunk/Misc/gdbinit そしてそれを入れてください
~/.gdbinit
編集して変更する[編集:不要になりました;リンクされたファイルには、2010 年 1 月 14 日の時点ですでにこの変更が含まれています]PyEval_EvalFrame
にPyEval_EvalFrameEx
gdb をアタッチします。
gdb -p PID
Python スタック トレースを取得します。
pystack
これをコメントとして追加します ハリドフの反応, 、しかし、私にはそうするための評判がありません。
私たちの中には、まだ Python の 2.6 より古いバージョン (Thread.ident に必要) にこだわっている人もいるので、次のようにコードを Python 2.5 で動作させるようにしました (ただし、スレッド名は表示されません)。
import traceback
import sys
def dumpstacks(signal, frame):
code = []
for threadId, stack in sys._current_frames().items():
code.append("\n# Thread: %d" % (threadId))
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
if line:
code.append(" %s" % (line.strip()))
print "\n".join(code)
import signal
signal.signal(signal.SIGQUIT, dumpstacks)
python -dv yourscript.py
これにより、インタープリターがデバッグ モードで実行され、インタープリターの動作のトレースが得られます。
コードを対話的にデバッグしたい場合は、次のように実行する必要があります。
python -m pdb yourscript.py
これは、Python インタプリタに、Python デバッガであるモジュール「pdb」を使用してスクリプトを実行するように指示します。このように実行すると、インタプリタは GDB と同様に対話モードで実行されます。
を見てください。 faulthandler
モジュール、Python 3.3 の新機能。あ faulthandler
バックポート Python 2 で使用するためのは PyPI で入手できます。
Solaris では、pstack(1) を使用できます。Python コードを変更する必要はありません。例えば。
# pstack 16000 | grep : | head
16000: /usr/bin/python2.6 /usr/lib/pkg.depotd --cfg svc:/application/pkg/serv
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:282 (_wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:295 (wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:242 (block) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/_init_.py:249 (quickstart) ]
[ /usr/lib/pkg.depotd:890 (<module>) ]
[ /usr/lib/python2.6/threading.py:256 (wait) ]
[ /usr/lib/python2.6/Queue.py:177 (get) ]
[ /usr/lib/python2.6/vendor-packages/pkg/server/depot.py:2142 (run) ]
[ /usr/lib/python2.6/threading.py:477 (run)
etc.
Linux システムを使用している場合は、次の優れた機能を使用してください。 gdb
Python デバッグ拡張機能を使用 ( python-dbg
または python-debuginfo
パッケージ)。マルチスレッド アプリケーション、GUI アプリケーション、C モジュールにも役立ちます。
以下を使用してプログラムを実行します。
$ gdb -ex r --args python <programname>.py [arguments]
これは指示します gdb
準備する python <programname>.py <arguments>
そして r
ユニット。
プログラムがハングしたら、に切り替えます。 gdb
コンソール、押します Ctr+C そして実行します:
(gdb) thread apply all py-list
スレッドをデバッグするためのソリューションをしばらく探していましたが、haridsv のおかげでここでそれを見つけました。ここでは、traceback.print_stack() を使用して少し簡略化したバージョンを使用します。
import sys, traceback, signal
import threading
import os
def dumpstacks(signal, frame):
id2name = dict((th.ident, th.name) for th in threading.enumerate())
for threadId, stack in sys._current_frames().items():
print(id2name[threadId])
traceback.print_stack(f=stack)
signal.signal(signal.SIGQUIT, dumpstacks)
os.killpg(os.getpgid(0), signal.SIGQUIT)
私の必要に応じて、スレッドを名前でフィルタリングすることもできます。
実行中の Python プロセスに接続し、コードを挿入して Python シェルを取得するツールをハッキングしました。
ここを参照してください: https://github.com/albertz/pydbattach
ピリンジ 実行中の Python プロセスと対話したり、スタック トレースや変数などを出力したりできるデバッガーです。事前の設定なしで。
私はこれまでシグナル ハンドラー ソリューションをよく使用してきましたが、特定の環境では問題を再現することが依然として難しい場合があります。
実行中の Python プロセスにフックして適切な結果を得る方法はありません。プロセスがロックアップした場合に私が行うことは、strace をフックして、何が起こっているのかを正確に把握しようとすることです。
残念ながら、多くの場合、strace は競合状態を「修正」するオブザーバーであるため、出力も役に立ちません。
使用できます PuDB, 、これを行うためのcursesインターフェイスを備えたPythonデバッガ。追加するだけです
from pudb import set_interrupt_handler; set_interrupt_handler()
コードを編集し、中断したい場合は Ctrl+C を使用します。続行できます c
失敗してもう一度やり直したい場合は、何度も中断します。
関数をデバッグする方法 コンソールで:
関数の作成 あなたはどこで pdb.set_trace() を使用する, 、次にデバッグしたい関数を選択します。
>>> import pdb
>>> import my_function
>>> def f():
... pdb.set_trace()
... my_function()
...
次に、作成した関数を呼び出します。
>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb)
デバッグを楽しんでください:)
似たようなものを知りません SIGQUITに対するJavaの応答, そのため、アプリケーションに組み込む必要がある場合があります。おそらく、ある種のメッセージへの応答でスタックトレースを取得できるサーバーを別のスレッドに作成できるでしょうか?
検査モジュールを使用します。
インポートヘルプヘルプ(Inspect.Stack)モジュールインスプタの機能スタックのヘルプ:
スタック(コンテキスト= 1)発信者のフレームの上のスタックのレコードのリストを返します。
本当にとても役に立ちます。
Python 3 では、デバッガーで c(ont(inue)) を初めて使用するときに、pdb によってシグナル ハンドラーが自動的にインストールされます。その後 Control-C を押すと、すぐに元の状態に戻ります。Python 2 では、比較的古いバージョンでも動作するワンライナーを次に示します (2.7 でテストしましたが、Python ソースを 2.4 に戻して確認したところ、問題ないようでした)。
import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))
Python のデバッグにある程度の時間を費やす場合は、pdb を学習する価値があります。インターフェイスは少しわかりにくいですが、gdb などの同様のツールを使用したことがある人には馴染みがあるはずです。
uWSGI を使用してこれを行う必要がある場合には、 Python トレースバッカー ビルトインされており、設定で有効にするだけです (各ワーカーの名前に番号が付いています)。
py-tracebacker=/var/run/uwsgi/pytrace
これを完了すると、ソケットに接続するだけでバックトレースを出力できます。
uwsgi --connect-and-read /var/run/uwsgi/pytrace1
私は Python 拡張機能を使用する GDB 陣営にいます。フォローする https://wiki.python.org/moin/DebuggingWithGdb, 、つまり
dnf install gdb python-debuginfo
またはsudo apt-get install gdb python2.7-dbg
gdb python <pid of running process>
py-bt
も検討してください info threads
そして thread apply all py-bt
.