web-dev-qa-db-ja.com

実行中のPythonアプリケーションからのスタックトレースの表示

このPythonアプリケーションはときどき動かなくなってしまい、どこにあるのかわかりません。

Pythonインタープリターにシグナルを送信して、実行中の正確なコードを表示する方法はありますか?

ある種のオンザフライのスタックトレース?

関連する質問:

320
Seb

このような状況で使用するモジュールがあります。プロセスは長時間実行されますが、不明で再現不可能な理由で時々スタックすることがあります。それは少しハックで、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()関数を呼び出し(すべてのpythonプログラムに使用させるためにsite.pyに固定することもできます)、実行させます。任意の時点で、killを使用するか、PythonでプロセスにSIGUSR1シグナルを送信します。

    os.kill(pid, signal.SIGUSR1)

これにより、プログラムは現在の時点でpythonコンソールにブレークし、スタックトレースを表示し、変数を操作できるようになります。 control-d(EOF)を使用して実行を継続します(ただし、おそらくシグナルを送信した時点ですべてのI/Oなどを中断するため、完全に非侵入的ではないことに注意してください)。

同じことを行う別のスクリプトがありますが、パイプを介して実行中のプロセスと通信します(バックグラウンドプロセスなどのデバッグを可能にするため)。ここに投稿するには少し大きいですが、 python cookbook recipe として追加しました。

305
Brian

シグナルハンドラーをインストールすることをお勧めしますが、よく使用します。たとえば、 bzr はデフォルトでpdb.set_trace()を呼び出すSIGQUITハンドラーをインストールし、すぐに pdb プロンプトにドロップします。 (正確な詳細については bzrlib.breakin モジュールのソースを参照してください。)pdbを使用すると、現在のスタックトレースを取得できるだけでなく、変数などを検査することもできます。

ただし、シグナルハンドラをインストールする先見性のないプロセスをデバッグする必要がある場合があります。Linuxでは、gdbをプロセスにアタッチして、いくつかのgdbマクロでpythonスタックトレースを取得できます。 。 http://svn.python.org/projects/python/trunk/Misc/gdbinit~/.gdbinitに入れてから:

  • Gdbを添付:gdb -pPID
  • pythonスタックトレースを取得します:pystack

残念ながら完全に信頼できるわけではありませんが、ほとんどの場合は機能します。

最後に、straceを付加すると、プロセスが何をしているのかを知ることができます。

143
spiv

私はほとんど常に複数のスレッドを扱っており、メインスレッドは一般的にあまり多くのことをしていません。そのため、最も興味深いのは、すべてのスタックをダンプすることです(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)
69
haridsv

nprepared pythonプログラムのスタックトレースの取得、ストックpythonでの実行デバッグシンボルなしは、 pyrasite 。 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へのヒント。)

46
Nickolay
>>> import traceback
>>> def x():
>>>    print traceback.extract_stack()

>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]

スタックトレースを適切にフォーマットすることもできます。 docs を参照してください。

Edit:@Douglas Leederによって提案されたJavaの動作をシミュレートするには、これを追加します。

import signal
import traceback

signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))

アプリケーションの起動コードに。その後、SIGUSR1を実行中のPythonプロセスに送信して、スタックを印刷できます。

34
Torsten Marek

traceback モジュールには、いくつかの素晴らしい機能があります:print_stack:

import traceback

traceback.print_stack()
27
gulgi

faulthandler module を試すことができます。 pip install faulthandlerを使用してインストールし、以下を追加します。

import faulthandler, signal
faulthandler.register(signal.SIGUSR1)

プログラムの開始時に。次に、SIGUSR1をプロセス(例:kill -USR1 42)に送信して、すべてのスレッドのPythonトレースバックを標準出力に表示します。 ドキュメントを読む その他のオプション(例:ファイルへのログイン)およびトレースバックを表示する他の方法について。

モジュールはPython 3.3の一部になりました。 Python 2については、 http://faulthandler.readthedocs.org/ を参照してください

23
haypo

ここで本当に役立ったのは、準備されていないPythonからスタックトレースを取得するための spivのヒント (評価ポイントがあれば投票してコメントする)です。プロセス。ただし、_ gdbinitスクリプトを修正するまで動作しませんでした 。そう:

  • http://svn.python.org/projects/python/trunk/Misc/gdbinit をダウンロードして、~/.gdbinitに入れます

  • 編集して、PyEval_EvalFramePyEval_EvalFrameExに変更します [編集:不要になりました。 2010-01-14現在、リンクされたファイルには既にこの変更があります]

  • Gdbを添付:gdb -p PID

  • pythonスタックトレースを取得します:pystack

20

これを 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)

python -dv yourscript.py

これにより、インタプリタがデバッグモードで実行され、インタプリタが実行していることのトレースが提供されます。

コードをインタラクティブにデバッグする場合は、次のように実行する必要があります。

python -m pdb yourscript.py

これは、pythonインタープリターにpythonデバッガーであるモジュール「pdb」でスクリプトを実行するように指示します。インタープリターが対話モードで実行されるように実行すると、 GDB

11
Gustavo Rubio

faulthandler モジュール、Python 3.3の新機能をご覧ください。 faulthandler backport Python 2で使用するには、PyPIで利用可能です。

10
Matt Joiner

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.
7
Tim Foster

私はスレッドをデバッグするソリューションを探していましたが、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)

必要に応じて、スレッドを名前でフィルタリングします。

6
Stefan

Linuxシステムを使用している場合は、gdbの素晴らしい機能をPythonデバッグ拡張機能(python-dbgまたはpython-debuginfoパッケージに含めることができます)で使用してください。また、マルチスレッドアプリケーション、GUIアプリケーション、Cモジュールにも役立ちます。

次を使用してプログラムを実行します。

$ gdb -ex r --args python <programname>.py [arguments]

これは、python <programname>.py <arguments>gdbunを準備するようにrに指示します。

プログラムがハングしたら、gdbコンソールに切り替えて、 Ctr+C そして実行:

(gdb) thread apply all py-list

セッション例 および詳細情報 here および here を参照してください。

6

実行中のPythonプロセスにアタッチし、Python Shellを取得するためのコードを挿入するツールをハッキングしました。

こちらをご覧ください: https://github.com/albertz/pydbattach

3
Albert

Pydb 、「gdbコマンドセットに基づいた大まかなPythonデバッガーの拡張バージョン」を見る価値があります。指定されたシグナルが送信されたときにデバッガーの起動を処理できるシグナルマネージャーが含まれています。

2006 Summer of Codeプロジェクトは、リモートデバッグ機能を mpdb というモジュールでpydbに追加することを検討しました。

3
rcoup

pyringe は、事前にセットアップすることなく、実行中のpythonプロセスと対話したり、スタックトレースや変数などを出力したりできるデバッガーです。

過去にシグナルハンドラソリューションをよく使用していましたが、特定の環境で問題を再現することは依然として困難な場合があります。

2
Dan Lecocq

実行中のpythonプロセスにフックして妥当な結果を得る方法はありません。プロセスがロックした場合に私がやることは、straceをフックして、何が起きているのかを正確に把握しようとすることです。

残念なことに、多くの場合、straceは競合状態を「修正」するオブザーバーであり、出力もそこで使用できなくなります。

1
Armin Ronacher

PuDB 、cursesインターフェイスを備えたPythonデバッガーを使用してこれを行うことができます。追加するだけ

from pudb import set_interrupt_handler; set_interrupt_handler()

コードに追加し、中断する場合はCtrl-Cを使用します。 cを続行し、見逃してもう一度やり直したい場合は、複数回中断できます。

1
asmeurer

関数をデバッグする方法コンソールで

関数の作成ここで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) 

楽しいデバッグ:)

1
jakvb

私はpython拡張機能を使用してGDBキャンプにいます。 https://wiki.python.org/moin/DebuggingWithGdb に従います。これは、

  1. dnf install gdb python-debuginfoまたはSudo apt-get install gdb python2.7-dbg
  2. gdb python <pid of running process>
  3. py-bt

また、info threadsthread apply all py-btも検討してください。

0
user7610

検査モジュールを使用します。

import inspect help(inspect.stack)モジュール検査の関数スタックに関するヘルプ:

stack(context = 1)呼び出し元のフレームの上のスタックのレコードのリストを返します。

本当にとても助かります。

0
Bhaskar

UWSGIでこれを行う必要がある場合、 Python Tracebacker ビルトインがあり、設定で有効にするだけです(番号は各ワーカーの名前に付加されます)。

py-tracebacker=/var/run/uwsgi/pytrace

これを実行したら、ソケットに接続するだけでバックトレースを印刷できます。

uwsgi --connect-and-read /var/run/uwsgi/pytrace1
0
Michal Čihař

SIGQUITに対するJavaの応答 に似たものは知らないので、アプリケーションに組み込む必要があるかもしれません。ある種のメッセージへの応答でスタックトレースを取得できる別のスレッドでサーバーを作成できますか?

0
Douglas Leeder

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などの同様のツールを使用したことのある人ならなじみがあるはずです。

0
jtatum