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


340

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

実行中の正確なコードを表示するようにPythonインタープリターに通知する方法はありますか?

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

関連する質問:



回答:


315

私はこのような状況で使用するモジュールを持っています-プロセスが長時間実行されますが、未知の再現不可能な理由で時々スタックします。これは少しハックで、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クックブックのレシピとして追加しました。


1
ありがとう!これはまさに私が探していたものです。たぶん、いくつかのPythonスニペットサイトにパイプサポート付きのスクリプトを投稿することもできますか?
Seb

2
これをpythonクックブックサイトに投稿しました-リンクを追加しました。
ブライアン

1
履歴機能を有効にするには、「インポートreadline」を追加する必要がありました。
miracle2k 2009年

2
すばらしいヒントです。これは、「mypythonapp」という単語を含むすべてのプロセスにシグナルを送信するためにも機能します。pkill -SIGUSR1 -f mypythonapp
Alexander

10
アプリケーションがスタックしている場合、Pythonインタープリターループを実行して信号を処理できない可能性があります。faulthandlerモジュール(およびPyPIにあるそのバックポート)を使用して、インタープリターループが応答することを必要とせずにPythonスタックを出力するCレベルのシグナルハンドラーを作成します。
gps 2014年

146

シグナルハンドラをインストールするという提案は良いものであり、私はそれを頻繁に使用しています。たとえば、bzrはデフォルトで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することで、プロセスが何をしているのかをよく知ることができます。


2
鮮やかさ!pystackコマンドは時々ロックしますが、それが実行される前に、準備をする必要なく、Pythonコード行でプロセスの完全なスタックトレースを取得します。
muudscope

26
マイナーアップデート:このgdbテクニック(および更新されたコード)はwiki.python.org/moin/DebuggingWithGdbに文書化されています。
ネルソン

7
私の知る限り、これは本当にデバッグシンボルがpythonバイナリにコンパイルされている場合にのみ機能します。たとえば、プログラムをpython2-dbgで実行しました(Ubuntuでは、これは別のパッケージにありますpython-dbg)。これらの記号がないと、あまり有用な情報が得られないようです。
drevicko 2013

1
私の場合、これUnable to locate python frameは各コマンドに戻ります
seriyPS 2014年

6
gdb 7+ --with-pythonサポートはpython-gdb.pyによって提供されます。詳細はこちら:chezsoi.org/lucas/blog/2014/11/07/en-gdb-python-macros
Lucas Cimon

71

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

53

スタックトレースの取得準備ができていない株式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へのヒントには、これへのポインタが含まれていました。)


5
これは私にとってはうまくいきました。dump_stacks.py単純にimport traceback; traceback.print_stack()
John Lehmann

2
traceback -l使用できる定義済みのPythonスクリプトのリストを提供しますdump_stacks.py。これはその1つです。独自のものを使用している場合(たとえば、スタックトレースをファイルに書き込む場合)、別の名前を使用することをお勧めします。
johndodo 2015

12
重要なヒント:apt-get install gdb python-dbgピラサイトを実行する前に(または同等のものを)実行してください。実行しない場合、警告なしに失敗します。それ以外の場合は魅力のように機能します!
johndodo 2015

ピラサイトの最後のリリースは2012年
ボリス

35
>>> 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プロセスに送信してスタックを印刷できます。


2
これはメインスレッドのバックトレースのみを出力します。すべてのスレッドのトレースを表示するための解決策はまだ見つけていません。実際、threading.enumerate()はすべてのThreadオブジェクトにアクセスできますが、PythonにはThreadオブジェクトからスタックを取得するためのAPIがないようです。
haridsv 2010

これはcygwinでうまく動作します。ただし、スタックトレースは3行しか出力されませんが、手掛かりを得るためにはそれで十分です
スラッシュドット08

28

トレースバックモジュールは、それらの間で、いくつかの素晴らしい機能を持っていますprint_stackを:

import traceback

traceback.print_stack()

1
ファイルの使用にスタックトレースを書き出すために: import traceback; f = open('/tmp/stack-trace.log', 'w') traceback.print_stack(file=f) f.close()
GuruM

1
彼の使いやすい答えのために@gulgiに+1します。他の回答のいくつかは、スクリプトの関数からコールスタックトレースを取得するという私の単純なタスクでは非常に複雑に見えました。
GuruM 2012

24

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

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

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

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


20

ここで私を本当に助けたのは、準備されていない Pythonプロセスからスタックトレースを取得するためのspivのヒント(評判ポイントがある場合は投票してコメントする)です。ただし、gdbinitスクリプト変更するまで機能しませんでした。そう:

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

  • 変更、それを編集PyEval_EvalFrameしますPyEval_EvalFrameEx[編集:もう必要ありません。リンクされたファイルには2010-01-14の時点ですでにこの変更があります]

  • gdbをアタッチします。 gdb -p PID

  • Pythonスタックトレースを取得します。 pystack


上記のURLのgdbinitには、提案されたパッチがすでにあるようです。私の場合、pystackと入力すると、CPUがハングしました。なぜだかわかりません。
Jesse Glick

2
いいえ、そうではありません。その線が3か所に表示されているため、わかりませんでした。リンクしたパッチは、この作品を見たときに変更したパッチを示しています。
Gunnlaugur Briem、

2
@spivの回答と同様に、これにはプログラムがデバッグシンボルでコンパイルされたpythonで実行する必要があります。それ以外の場合は単に取得しますNo symbol "co" in current context.
ニコライ

12

これをコメントとしてharidsvの応答に追加しますが、そうする評判はありません。

一部の人はまだ2.6より前のバージョンのPython(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)

11

python -dv yourscript.py

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

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

python -m pdb yourscript.py

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


これは質問の答えにはなりません。問題は、すでに実行中のプロセスに関するものでした。
dbn 2014年

11

faulthandlerPython 3.3の新しいモジュールを見てください。Python 2で使用するためのfaulthandlerバックポートがPyPIで利用可能です。


2
@haypoによるより最近の回答はこれをより詳細にカバーしています。これが通常SOでどのように処理されるかはわかりませんが、2つの本質的に重複する答えを持っているのは間違っていると感じます...
Nickolay

7

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.

2
pstack同じことをするDebian / Ubuntuプログラムがあるようです
Rory

1
ファイル名と行番号を含むPythonトレースバックではなく、Linuxでのみバックトレースを提供するようです。
ogrisel 2013年

6

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

ここここサンプルセッションと詳細を参照してください。


6

私はスレッドをデバッグするための解決策をしばらく探していましたが、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)

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


3

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

2006年夏のコードのプロジェクトと呼ばれるモジュールにpydbにリモートデバッグ機能を追加して見mpdb


それは2つ(を経ていたようだ1)書き換え(2追加することなく、添付-PIDでは、私が...探していた特色
Nickolay


3

これは、優れたpy-spyで実行できます。これはPythonプログラムのサンプリングプロファイラーなので、その役割はPythonプロセスにアタッチして、そのコールスタックをサンプリングすることです。したがって、プロセスpy-spy dump --pid $SOME_PID内のすべてのスレッドの呼び出しスタックをダンプするために必要なのはこれだけです$SOME_PID。通常、エスカレートされた特権が必要です(ターゲットプロセスのメモリを読み取るため)。

以下は、スレッド化されたPythonアプリケーションの例です。

$ sudo py-spy dump --pid 31080
Process 31080: python3.7 -m chronologer -e production serve -u www-data -m
Python v3.7.1 (/usr/local/bin/python3.7)

Thread 0x7FEF5E410400 (active): "MainThread"
    _wait (cherrypy/process/wspbus.py:370)
    wait (cherrypy/process/wspbus.py:384)
    block (cherrypy/process/wspbus.py:321)
    start (cherrypy/daemon.py:72)
    serve (chronologer/cli.py:27)
    main (chronologer/cli.py:84)
    <module> (chronologer/__main__.py:5)
    _run_code (runpy.py:85)
    _run_module_as_main (runpy.py:193)
Thread 0x7FEF55636700 (active): "_TimeoutMonitor"
    run (cherrypy/process/plugins.py:518)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)
Thread 0x7FEF54B35700 (active): "HTTPServer Thread-2"
    accept (socket.py:212)
    tick (cherrypy/wsgiserver/__init__.py:2075)
    start (cherrypy/wsgiserver/__init__.py:2021)
    _start_http_thread (cherrypy/process/servers.py:217)
    run (threading.py:865)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)
...
Thread 0x7FEF2BFFF700 (idle): "CP Server Thread-10"
    wait (threading.py:296)
    get (queue.py:170)
    run (cherrypy/wsgiserver/__init__.py:1586)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)  

2

pyringeは、事前の設定なしで、実行中のpythonプロセス、スタックトレース、変数などを印刷できるデバッガです。

過去にシグナルハンドラーソリューションを頻繁に使用しましたが、特定の環境で問題を再現するのが難しい場合もあります。


3
どうやらそれは特定のgdbビルド(たとえば、ubuntuにインストールしたもの)と互換性がありません:github.com/google/pyringe/issues/16、手動で再構築する必要があります。別のデバッガーはpyrasite、私にとって魅力的なものでした。
Nickolay

1

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

残念なことに、straceは、競合状態を「修正」して出力がそこでも役に立たないようにするオブザーバーであることがよくあります。


1
ええ、これは本当です。thad pdbが実行中のプロセスへのアタッチをサポートしていないのは残念です...
BartoszRadaczyńskiSep

本当じゃない。上記の「spiv」による回答を参照してください。これは、gdbを接続してPythonスタックトレースを取得する方法を示しています。
Andrew Cooke、

それは同じではありません-それらのgdbマクロは信頼性がなく、pdbの完全なパワー/使い慣れたインターフェースを提供しません。私は、ptraceを使用して実行中のPythonプロセスにPythonバイトコードを挿入し、 'import pdb; pdb.set_trace() '、おそらくsys.stdin / stdoutを一時的にリダイレクトした後。
Marius Gedminas、2010

これはもう当てはまりません。pyringe/ pyrasiteを指す他の回答を参照してください。
Nickolay 2015

1

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

from pudb import set_interrupt_handler; set_interrupt_handler()

あなたのコードにあなたがブレークしたいときにCtrl-Cを使用してください。c見逃して再試行したい場合は、続行して何度も中断できます。


あなたはジャンゴで上記のコマンドを使用すると、グリッチを防ぐために、適切にサーバを実行することを忘れないでください:「manage.pyのrunserver --noreload --nothreading」
potar

1

私は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 threadsしてくださいthread apply all py-bt


Traceback (most recent call first): Python Exception <class 'gdb.error'> No frame is currently selected.: Error occurred in Python command: No frame is currently selected.実行py-bt中のような応答を取得するのは正常gdbですか?
crookedleaf 2018

1
気にしないで。これは、私のアプリがとして実行されていたためですsudo。私gdb pyton <pid>もsudoとして実行する必要がありました。
crookedleaf 2018

1

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

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) 

幸せなデバッグ:)



0

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

import inspect help(inspect.stack)help in function stack in module inspect:

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

本当に助かります。


0

Python 3では、デバッガーでc(ont(inue))を初めて使用するときに、pdbが自動的にシグナルハンドラーをインストールします。その後Control-Cを押すと、そこに戻ります。Python 2で、比較的古いバージョンでも動作するはずの1行です(2.7でテスト済みですが、Pythonソースを2.4に戻し、問題がないように見えました)。

import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))

Pythonのデバッグに時間を費やす場合は、pdbを学ぶ価値があります。インターフェースは少し鈍いですが、gdbなどの同様のツールを使用したことがある人なら誰でもよく知っているはずです。


0

uWSGIでこれを行う必要がある場合は、Python Tracebackerが組み込まれており、構成で有効にするだけです(番号は各ワーカーの名前に付加されます)。

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

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

uwsgi --connect-and-read /var/run/uwsgi/pytrace1

0

コードが実行された時点で、この小さなスニペットを挿入して、適切にフォーマットされた印刷スタックトレースを表示できます。logsプロジェクトのルートディレクトリに呼び出されるフォルダーがあることを前提としています。

# DEBUG: START DEBUG -->
import traceback

with open('logs/stack-trace.log', 'w') as file:
    traceback.print_stack(file=file)
# DEBUG: END DEBUG --!
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.