私は時々立ち往生しているこのPythonアプリケーションを持っていて、どこにいるのかわかりません。
実行中の正確なコードを表示するようにPythonインタープリターに信号を送る方法はありますか?
ある種のオンザフライスタックトレース?
関連する質問:
私は時々立ち往生しているこの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クックブックレシピとして追加しました。
シグナルハンドラーをインストールするという提案は良いもので、私はそれをよく使います。たとえば、bzrはデフォルトで SIGQUIT ハンドラーをインストールし、これを呼び出しpdb.set_trace()
てすぐにpdbプロンプトにドロップします。(正確な詳細については、bzrlib.breakinモジュールのソースを参照してください。) pdb を使用すると、(コマンドを使用して) 現在のスタック トレースを取得できるだけでなく、(w)here
変数などを検査することもできます。
ただし、シグナル ハンドラーをインストールするための先見の明がなかったプロセスをデバッグする必要がある場合があります。http://svn.python.org/projects/python/trunk/Misc/gdbinitを入れてから:~/.gdbinit
gdb -p
PID
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)
>>> import traceback
>>> def x():
>>> print traceback.extract_stack()
>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]
スタック トレースを適切にフォーマットすることもできます。 docsを参照してください。
編集:@Douglas Leederが提案するように、Javaの動作をシミュレートするには、これを追加します:
import signal
import traceback
signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))
アプリケーションのスタートアップ コードに追加します。SIGUSR1
次に、実行中の Python プロセスに送信することで、スタックを出力できます。
tracebackモジュールにはいくつかの優れた機能があります。
import traceback
traceback.print_stack()
ここで本当に助けになったのは、準備されていないPythonプロセスからスタックトレースを取得するためのspivのヒント(レピュテーションポイントがあれば投票してコメントする)です。gdbinitスクリプトを変更するまで機能しなかったことを除いて。それで:
http://svn.python.org/projects/python/trunk/Misc/gdbinitをダウンロードして、~/.gdbinit
編集して、に変更[編集:もう必要ありません; リンクされたファイルには、2010-01-14の時点ですでにこの変更があります]PyEval_EvalFrame
しますPyEval_EvalFrameEx
gdbをアタッチします:gdb -p PID
Pythonスタックトレースを取得します。pystack
python -dv yourscript.py
これにより、インタプリタがデバッグモードで実行され、インタプリタが実行していることのトレースが得られます。
コードをインタラクティブにデバッグする場合は、次のように実行する必要があります。
python -m pdb yourscript.py
これは、Pythonデバッガーであるモジュール「pdb」を使用してスクリプトを実行するようにPythonインタープリターに指示します。このように実行すると、インタープリターはGDBのようにインタラクティブモードで実行されます。
これをコメントとしてharidsv の応答に追加しますが、そうする評判がありません。
私たちの中には、2.6 (Thread.ident に必要) よりも古いバージョンの Python にまだ行き詰まっている人もいるので、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)
faulthandler
Python 3.3 で新しく追加されたモジュールを見てみましょう。Python 2 で使用するfaulthandler
バックポートは 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.
スレッドをデバッグするための解決策をしばらく探していましたが、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)
必要に応じて、スレッドを名前でフィルタリングします。
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
実行中の Python プロセスにアタッチし、コードを挿入して Python シェルを取得するツールをハックしました。
ここを参照してください: https://github.com/albertz/pydbattach
これを行うには、curses インターフェースを備えた Python デバッガーであるPuDBを使用できます。追加するだけ
from pudb import set_interrupt_handler; set_interrupt_handler()
コードに追加し、中断したい場合は Ctrl-C を使用します。c
失敗してやり直したい場合は、続行して何度でも中断できます。
私は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
ください。
SIGQUITに対するJavaの応答に似たものは何も知らないので、アプリケーションに組み込む必要があるかもしれません。ある種のメッセージへの応答でスタックトレースを取得できるサーバーを別のスレッドで作成できるかもしれません。
実行中の Python プロセスにフックして妥当な結果を得る方法はありません。プロセスがロックアップした場合に私が行うことは、strace を接続して、正確に何が起こっているのかを把握しようとすることです。
残念ながら、多くの場合、strace は競合状態を「修正」するオブザーバーであるため、そこでも出力が役に立たなくなります。
検査モジュールを使用します。
import inspect help(inspect.stack) モジュール inspect の関数スタックに関するヘルプ:
stack(context=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 Tracebackerが組み込まれており、構成で有効にするだけです (番号は各ワーカーの名前に付けられます)。
py-tracebacker=/var/run/uwsgi/pytrace
これが完了したら、ソケットに接続するだけでバックトレースを出力できます。
uwsgi --connect-and-read /var/run/uwsgi/pytrace1