Zaman zaman sıkışıp kalan bu Python uygulaması var ve nerede bulamıyorum.
Python yorumlayıcısına, çalışan kodun tam olarak gösterilmesini bildirmenin herhangi bir yolu var mı?
Bir tür anında yığın izlemesi?
İlgili sorular:
Zaman zaman sıkışıp kalan bu Python uygulaması var ve nerede bulamıyorum.
Python yorumlayıcısına, çalışan kodun tam olarak gösterilmesini bildirmenin herhangi bir yolu var mı?
Bir tür anında yığın izlemesi?
İlgili sorular:
Yanıtlar:
Ben böyle bir durum için kullandığım modül var - bir süreç uzun süre çalışacak ama bazen bilinmeyen ve geri alınamaz nedenlerle sıkışıp kalır. Biraz hacky ve sadece unix üzerinde çalışır (sinyal gerektirir):
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
Kullanmak için, programınız başladığında bir noktada listen () işlevini çağırın (hatta tüm python programlarının kullanması için site.py içine yapıştırabilirsiniz) ve çalışmasına izin verin. Herhangi bir noktada, işlemi kill veya python kullanarak bir SIGUSR1 sinyali gönderin:
os.kill(pid, signal.SIGUSR1)
Bu, programın şu anda bulunduğu noktada bir python konsoluna girmesine, yığın izlemesini göstermesine ve değişkenleri değiştirmenize izin verir. Çalışmaya devam etmek için control-d'yi (EOF) kullanın (ancak işaret ettiğiniz noktada muhtemelen herhangi bir G / Ç vb. Keseceğinizi unutmayın, bu yüzden tamamen müdahaleci değildir.
Aynı şey yapan başka bir komut dosyası var, ancak bir boru üzerinden çalışan işlemle iletişim kurar (arka plandaki işlemlerin hata ayıklamasına izin vermek vb.). Burada göndermek biraz büyük, ama ben bir python yemek tarifi olarak ekledim .
faulthandler
Tercüman döngüsünün yanıt vermesini gerektirmeden Python yığınını yazdıracak bir C seviyesi sinyal işleyicisi için modülü (ve PyPI'de bulunan arka portunu) kullanın.
Bir sinyal işleyici takma önerisi iyi bir şey ve çok kullanıyorum. Örneğin, bzr varsayılan olarak pdb.set_trace()
sizi hemen bir pdb istemine bırakmaya çalışan bir SIGQUIT işleyicisi yükler . ( Kesin ayrıntılar için bzrlib.breakin modülünün kaynağına bakın.) PDB ile yalnızca geçerli yığın izlemesini almakla kalmaz, aynı zamanda değişkenleri vb.
Ancak, bazen sinyal işleyicisini yüklemek için öngörüsü olmayan bir işlemde hata ayıklamak gerekir. Linux'ta, işleme gdb ekleyebilir ve bazı gdb makrolarıyla bir python yığın izlemesi alabilirsiniz. Put http://svn.python.org/projects/python/trunk/Misc/gdbinit içinde ~/.gdbinit
, sonra:
gdb -p
PID
pystack
Ne yazık ki tamamen güvenilir değil, ancak çoğu zaman işe yarıyor.
Son olarak, takmak strace
genellikle bir sürecin ne yaptığını size iyi bir fikir verebilir.
python-dbg
). Bu semboller olmadan, pek yararlı bilgi elde edemezsiniz.
Unable to locate python frame
her komuta dönüş
Neredeyse her zaman birden fazla iş parçacığı ile uğraşıyorum ve ana iş parçacığı genellikle çok şey yapmıyor, bu yüzden en ilginç olanı tüm yığınları (Java'nın dökümü gibi) dökümüdür. İşte bu blogu temel alan bir uygulama :
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)
Bir bir yığın izleme alma hazırlıksız bir stok piton çalışan, piton programı sembollerini ayıklama olmadan ile yapılabilir pyrasite . Ubuntu Trusty'de benim için bir cazibe gibi çalıştı:
$ 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
(Diğer araçların yanı sıra, cevabı buna işaret eden @Albert'e şapka ucu.)
dump_stacks.py
olduimport traceback; traceback.print_stack()
traceback -l
kullanabileceğiniz önceden tanımlanmış python betiklerinin bir listesini verir ve dump_stacks.py
bunlardan biridir. Kendinizinkini kullanıyorsanız (örneğin bir dosyaya yığın izlemesi yazmak için) farklı bir ad kullanmak akıllıca olabilir.
apt-get install gdb python-dbg
pyrasite çalıştırmadan önce (veya eşdeğeri) çalıştırın, aksi takdirde sessizce başarısız olur. Aksi takdirde cazibe gibi çalışır!
>>> import traceback
>>> def x():
>>> print traceback.extract_stack()
>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]
Ayrıca yığın izini güzelce biçimlendirebilirsiniz, belgelere bakın .
Düzenleme : @Douglas Leeder tarafından önerilen Java davranışını simüle etmek için şunu ekleyin:
import signal
import traceback
signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))
uygulamanızdaki başlangıç koduna. Ardından SIGUSR1
, çalışan Python işlemine göndererek yığını yazdırabilirsiniz .
Geri izleme modülünün bazı güzel işlevleri vardır: print_stack:
import traceback
traceback.print_stack()
import traceback; f = open('/tmp/stack-trace.log', 'w') traceback.print_stack(file=f) f.close()
Arıza giderici modülünü deneyebilirsiniz . Kullanarak yükleyin pip install faulthandler
ve ekleyin:
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
programınızın başında. Ardından kill -USR1 42
, standart çıktıya tüm iş parçacıklarının Python izlemesini görüntülemek için SIGUSR1'i işleminize (ör :) gönderin. Daha fazla seçenek (örn: bir dosyaya giriş) ve geri izlemeyi görüntülemenin diğer yolları için belgeleri okuyun .
Modül şimdi Python 3.3'ün bir parçası. Python 2 için bkz. Http://faulthandler.readthedocs.org/
Burada gerçekten bana yardımcı olan, hazırlıksız bir Python işleminden bir yığın izini almak için spiv'in ipucu (oylama ve itibar puanlarım hakkında yorum yapmam) . Ben gdbinit betiğini değiştirene kadar işe yaramadı . Yani:
indirmek http://svn.python.org/projects/python/trunk/Misc/gdbinit ve koyun~/.gdbinit
Değişen, düzenlemeden [değiştir: artık gerekli değil; bağlantılı dosyada 2010-01-14 itibarıyla bu değişiklik zaten var]PyEval_EvalFrame
içinPyEval_EvalFrameEx
GDB ekle: gdb -p PID
Python yığını izini alın: pystack
No symbol "co" in current context.
Bu haridsv yanıt için bir yorum olarak eklemek istiyorum , ama bunu yapmak için itibar eksikliği:
Bazılarımız hala Python 2.6 (eski Thread.ident için gerekli) eski bir sürümü üzerinde sıkışmış, bu yüzden Python 2.5 (iş parçacığı adı görüntülenmeden olsa da) çalışma kodu var:
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
Bu, yorumlayıcının hata ayıklama modunda çalışmasını ve yorumlayıcının ne yaptığını izlemenizi sağlayacaktır.
Kodda etkileşimli olarak hata ayıklamak istiyorsanız, kodu şu şekilde çalıştırmalısınız:
python -m pdb yourscript.py
Bu, python yorumlayıcısına komut dosyanızı python hata ayıklayıcısı olan "pdb" modülü ile çalıştırmasını söyler, eğer yorumlayıcı GDB'ye benzer şekilde etkileşimli modda yürütülecek şekilde çalıştırırsanız
faulthandler
Python 3.3'te yeni olan modüle bir göz atın . faulthandler
PyPI'de Python 2'de kullanım için bir backport bulunmaktadır.
Solaris'te pstack (1) kullanabilirsiniz. Python kodunda değişiklik yapmanız gerekmez. Örneğin.
# 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.
pstack
Aynı şeyi yapan bir Debian / Ubuntu programı var gibi görünüyor
Bir Linux sistemindeyseniz, gdb
Python hata ayıklama uzantılarıyla (içinde python-dbg
veya python-debuginfo
paketinde olabilir) mükemmelliğini kullanın. Ayrıca çok iş parçacıklı uygulamalara, GUI uygulamalarına ve C modüllerine yardımcı olur.
Programınızı aşağıdakilerle çalıştırın:
$ gdb -ex r --args python <programname>.py [arguments]
Bu, onu gdb
hazırlamak python <programname>.py <arguments>
ve birleştirmek için talimat verir r
.
Şimdi program askıda kaldığınızda, gdb
konsola geçin , tuşuna basın Ctr+Cve çalıştırın:
(gdb) thread apply all py-list
Bkz örnek oturumu ve daha fazla bilgi burada ve burada .
Bir süre konularımı hata ayıklamak için bir çözüm arıyordum ve haridsv sayesinde burada buldum. Ben traceback.print_stack () kullanarak biraz basitleştirilmiş sürümünü kullanın:
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)
Benim ihtiyaçları için de konuya göre filtre.
"Python hata ayıklayıcının gdb komut kümesine dayanan genişletilmiş bir sürümü" olan Pydb'ye bakmaya değer . Belirli bir sinyal gönderildiğinde hata ayıklayıcıyı başlatmaya dikkat edebilecek sinyal yöneticileri içerir.
2006 Yazı Kodu projesi, mpdb adlı bir modülde pydb'ye uzaktan hata ayıklama özellikleri eklemeyi inceledi .
Birlikte çalışan bir Python işlemi ekler ve Python kabuk almak için bazı kod enjekte bazı aracı kesmek.
Buraya bakın: https://github.com/albertz/pydbattach
pyrasite
mükemmel çalıştı!
Mükemmel py-spy ile yapılabilir . Bu , Python programları için bir örnekleme profili oluşturucusudur , bu nedenle görevi bir Python işlemine bağlanmak ve çağrı yığınlarını örneklemektir. Bu nedenle, işlemdeki py-spy dump --pid $SOME_PID
tüm iş parçacıklarının çağrı yığınlarını dökmek için yapmanız gereken tek şey $SOME_PID
. Genellikle yükseltilmiş ayrıcalıklara ihtiyaç duyar (hedef sürecin hafızasını okumak için).
Aşağıda, dişli bir Python uygulaması için nasıl göründüğüne bir örnek verilmiştir.
$ 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)
pyringe , herhangi bir önsel kurulum yapmadan çalışan python süreçleri, yazdırma yığını izleri, değişkenler vb. ile etkileşime girebilen bir hata ayıklayıcıdır.
Geçmişte sinyal işleyici çözümünü sık sık kullandığım halde, sorunu bazı ortamlarda yeniden oluşturmak hala zor olabilir.
pyrasite
benim için bir cazibe gibi çalıştı.
Çalışan bir python sürecine bağlanmanın ve makul sonuçlar elde etmenin bir yolu yoktur. Süreçler kilitlenirse ne yapmalıyım strace'yi kancalamak ve tam olarak ne olduğunu anlamaya çalışmaktır.
Ne yazık ki çoğu zaman strace, yarış koşullarını "düzelten" bir gözlemcidir, böylece çıktı orada da işe yaramaz.
Bunu yapmak için lanetler arabirimli bir Python hata ayıklayıcısı olan PuDB'yi kullanabilirsiniz . Sadece ekle
from pudb import set_interrupt_handler; set_interrupt_handler()
kodunuzu girin ve kırmak istediğinizde Ctrl-C tuşlarını kullanın. Eğer c
kaçırırsanız ve tekrar denemek istiyorsanız , devam edip birkaç kez kırabilirsiniz.
Python eklentileri ile GDB kampındayım. Takip https://wiki.python.org/moin/DebuggingWithGdb , araçlar
dnf install gdb python-debuginfo
veya sudo apt-get install gdb python2.7-dbg
gdb python <pid of running process>
py-bt
Ayrıca düşünün info threads
ve 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.
koşarken py-bt
olduğu gibi cevap almak normal gdb
mi?
sudo
. Ben gdb pyton <pid>
de sudo olarak çalıştırmak gerekiyordu .
Konsoldaki herhangi bir işlevde hata ayıklama :
İşlev oluşturun Eğer pdb.set_trace (kullanın) , o zaman hata ayıklama istiyorum çalışır.
>>> import pdb
>>> import my_function
>>> def f():
... pdb.set_trace()
... my_function()
...
Sonra oluşturulan işlevi çağırın:
>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb)
Mutlu hata ayıklama :)
Java'nın SIGQUIT'e yanıtına benzer bir şey bilmiyorum , bu yüzden uygulamanıza yerleştirmeniz gerekebilir. Belki başka bir iş parçacığında bir tür iletiye yanıt olarak bir yığın izini alabilir bir sunucu yapabilir?
Python 3'te, pdb hata ayıklayıcıda ilk kez c (ont (inue)) kullandığınızda otomatik olarak bir sinyal işleyici kuracaktır. Daha sonra Control-C tuşlarına basarsanız oraya geri dönersiniz. Python 2'de, nispeten eski versiyonlarda bile çalışması gereken bir astar var (2.7'de test edildi, ancak Python kaynağını 2.4'e kadar kontrol ettim ve iyi görünüyordu):
import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))
Pdhon hata ayıklamak için herhangi bir zaman harcamak pdb öğrenmeye değer. Arayüz biraz geniş ama gdb gibi benzer araçları kullanan herkese aşina olmalıdır.
Bunu uWSGI ile yapmanız gerekiyorsa, Python Tracebacker yerleşiktir ve sadece yapılandırmada etkinleştirilmesi gerekir (sayı her çalışanın adına eklenir):
py-tracebacker=/var/run/uwsgi/pytrace
Bunu yaptıktan sonra, sokete bağlayarak geri izi yazdırabilirsiniz:
uwsgi --connect-and-read /var/run/uwsgi/pytrace1
Kodun çalıştığı noktada, güzel biçimlendirilmiş yazdırılmış yığın izini görmek için bu küçük snippet'i ekleyebilirsiniz. logs
Projenizin kök dizininde adlı bir klasörünüz olduğunu varsayar .
# DEBUG: START DEBUG -->
import traceback
with open('logs/stack-trace.log', 'w') as file:
traceback.print_stack(file=file)
# DEBUG: END DEBUG --!