عرض تتبع المكدس من تطبيق Python قيد التشغيل
-
02-07-2019 - |
سؤال
لدي تطبيق 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
للاستخدام، ما عليك سوى استدعاء وظيفة الاستماع () في مرحلة ما عند بدء تشغيل برنامجك (يمكنك حتى لصقها في site.py لتستخدمها جميع برامج بايثون)، واتركها تعمل.في أي وقت، أرسل العملية إشارة SIGUSR1، باستخدام kill، أو في python:
os.kill(pid, signal.SIGUSR1)
سيؤدي هذا إلى انقطاع البرنامج عن وحدة تحكم بايثون عند النقطة التي هو فيها حاليًا، مما يعرض لك تتبع المكدس، ويتيح لك التعامل مع المتغيرات.استخدم control-d (EOF) لمواصلة التشغيل (على الرغم من ملاحظة أنك من المحتمل أن تقاطع أي إدخال/إخراج وما إلى ذلك عند النقطة التي تشير إليها، لذلك فهي ليست غير تدخلية تمامًا.
لدي برنامج نصي آخر يفعل نفس الشيء، باستثناء أنه يتصل بالعملية الجارية من خلال أنبوب (للسماح بتصحيح أخطاء العمليات الخلفية وما إلى ذلك).إنه كبير بعض الشيء للنشر هنا، لكنني أضفته كملف وصفة كتاب الطبخ بايثون.
نصائح أخرى
يعد اقتراح تثبيت معالج الإشارة فكرة جيدة، وأنا أستخدمه كثيرًا.على سبيل المثال، bzr بشكل افتراضي يقوم بتثبيت معالج SIGQUIT الذي يستدعي pdb.set_trace()
لإسقاطك على الفور في ملف pdb اِسْتَدْعَى.(انظر bzrlib.breakin للحصول على التفاصيل الدقيقة.) باستخدام pdb، لا يمكنك الحصول على تتبع المكدس الحالي فحسب، بل يمكنك أيضًا فحص المتغيرات، وما إلى ذلك.
ومع ذلك، أحتاج أحيانًا إلى تصحيح أخطاء عملية لم يكن لدي البصيرة لتثبيت معالج الإشارة فيها.في نظام التشغيل Linux، يمكنك إرفاق gdb بالعملية والحصول على تتبع مكدس python مع بعض وحدات ماكرو gdb.يضع 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)
الحصول على تتبع مكدس لـ غير مستعد برنامج بايثون، يعمل في بيثون الأسهم دون تصحيح الرموز يمكن القيام به مع بيراسيت.عملت مثل السحر بالنسبة لي على 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)]
يمكنك أيضًا تنسيق تتبع المكدس بشكل جيد، راجع ملف مستندات.
يحرر:لمحاكاة سلوك Java، كما اقترح @Douglas Leeder، أضف هذا:
import signal
import traceback
signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))
إلى رمز بدء التشغيل في التطبيق الخاص بك.ثم يمكنك طباعة المكدس عن طريق الإرسال SIGUSR1
إلى عملية بايثون الجارية.
ال تتبع الأثر تحتوي الوحدة على بعض الوظائف الرائعة، من بينها:طباعة_المكدس:
import traceback
traceback.print_stack()
يمكنك تجربة وحدة معالجة الأخطاء.تثبيته باستخدام pip install faulthandler
و أضف:
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
في بداية برنامجكثم أرسل SIGUSR1 إلى عمليتك (على سبيل المثال: kill -USR1 42
) لعرض تتبع Python لجميع سلاسل الرسائل إلى الإخراج القياسي. اقرأ الوثائق لمزيد من الخيارات (على سبيل المثال:تسجيل الدخول إلى ملف) وطرق أخرى لعرض التتبع.
أصبحت الوحدة الآن جزءًا من Python 3.3.بالنسبة لبيثون 2، انظر http://faulthandler.readthedocs.org/
ما ساعدني حقا هنا هو نصيحة spiv (والذي سأصوت عليه وأعلق عليه إذا كان لدي نقاط السمعة) للحصول على تتبع مكدس من ملف غير مستعد عملية بايثون.إلا أنها لم تنجح حتى أنا تعديل البرنامج النصي gdbinit.لذا:
تحميل http://svn.python.org/projects/python/trunk/Misc/gdbinit ووضعها فيه
~/.gdbinit
تحريره، والتغيير[يحرر:لم تعد هناك حاجة؛يحتوي الملف المرتبط بالفعل على هذا التغيير اعتبارًا من 14-01-2010]PyEval_EvalFrame
لPyEval_EvalFrameEx
إرفاق جي دي بي:
gdb -p PID
احصل على تتبع مكدس بايثون:
pystack
وأود أن أضيف هذا كتعليق على رد haridsv, ، لكني أفتقر إلى السمعة للقيام بذلك:
لا يزال البعض منا عالقًا في إصدار 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)
بيثون -dv yourscript.py
سيؤدي ذلك إلى تشغيل المترجم في وضع التصحيح ويعطيك تتبعًا لما يفعله المترجم.
إذا كنت تريد تصحيح التعليمات البرمجية بشكل تفاعلي، فيجب عليك تشغيلها على النحو التالي:
بايثون -m pdb yourscript.py
يخبر ذلك مترجم بايثون بتشغيل البرنامج النصي الخاص بك باستخدام الوحدة النمطية "pdb" وهي مصحح أخطاء بايثون، إذا قمت بتشغيله بهذه الطريقة فسيتم تنفيذ المترجم في الوضع التفاعلي، تمامًا مثل GDB
نلقي نظرة على faulthandler
الوحدة الجديدة في Python 3.3.أ faulthandler
backport للاستخدام في 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
طَرد).كما أنه يساعد في التطبيقات متعددة الخيوط وتطبيقات واجهة المستخدم الرسومية ووحدات C.
قم بتشغيل برنامجك باستخدام:
$ gdb -ex r --args python <programname>.py [arguments]
هذا يرشد gdb
للتحضير python <programname>.py <arguments>
و r
وحدة.
الآن عندما يتوقف البرنامج، قم بالتبديل إلى gdb
وحدة التحكم، اضغط السيطرة + C وتنفيذ:
(gdb) thread apply all py-list
يرى جلسة سبيل المثال والمزيد من المعلومات هنا و هنا.
كنت أبحث لفترة من الوقت عن حل لتصحيح أخطاء سلاسل الرسائل الخاصة بي ووجدته هنا بفضل haridsv.أستخدم إصدارًا مبسطًا قليلاً باستخدام التتبع.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)
لاحتياجاتي أقوم أيضًا بتصفية المواضيع حسب الاسم.
لقد قمت باختراق بعض الأدوات التي يتم ربطها بعملية بايثون قيد التشغيل وحقن بعض التعليمات البرمجية للحصول على غلاف بايثون.
انظر هنا: https://github.com/albertz/pydbatach
بيرينج هو مصحح أخطاء يمكنه التفاعل مع عمليات بايثون الجارية، وطباعة آثار المكدس، والمتغيرات، وما إلى ذلك.دون أي إعداد مسبق.
على الرغم من أنني استخدمت حل معالج الإشارة في كثير من الأحيان في الماضي، إلا أنه لا يزال من الصعب تكرار المشكلة في بيئات معينة.
لا توجد طريقة للربط بعملية بايثون قيد التشغيل والحصول على نتائج معقولة.ما أفعله في حالة قفل العمليات هو تثبيت المسار ومحاولة معرفة ما يحدث بالضبط.
لسوء الحظ، غالبًا ما يكون المراقب هو الذي "يصلح" ظروف السباق بحيث يكون الناتج عديم الفائدة هناك أيضًا.
يمكنك استخدام PuDB, ، مصحح أخطاء بايثون مع واجهة لعنات للقيام بذلك.فقط اضف
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)
التصحيح سعيد :)
لا أعرف شيئًا مشابهًا استجابة Java لـ SIGQUIT, ، لذلك قد يتعين عليك تضمينه في تطبيقك.ربما يمكنك إنشاء خادم في سلسلة محادثات أخرى يمكنه الحصول على تتبع مكدس عند الرد على رسالة من نوع ما؟
استخدم وحدة الفحص.
استيراد تعليمات التفتيش (inspect.stack) مساعدة في مكدس الوظائف في الوحدة النمطية تفقد:
المكدس (السياق = 1) إرجاع قائمة السجلات للمكدس فوق إطار المتصل.
أجد أنه من المفيد جدا في الواقع.
في Python 3، سيقوم pdb تلقائيًا بتثبيت معالج الإشارة في المرة الأولى التي تستخدم فيها c(ont(inue)) في مصحح الأخطاء.سيؤدي الضغط على Control-C بعد ذلك إلى إعادتك إلى هناك.في Python 2، إليك سطر واحد يجب أن يعمل حتى في الإصدارات القديمة نسبيًا (تم اختباره في 2.7 ولكني راجعت مصدر Python مرة أخرى إلى 2.4 وبدا جيدًا):
import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))
يستحق pdb التعلم إذا كنت تقضي وقتًا طويلاً في تصحيح أخطاء Python.الواجهة منفرجة بعض الشيء ولكن يجب أن تكون مألوفة لأي شخص استخدم أدوات مماثلة، مثل gdb.
في حال كنت بحاجة إلى القيام بذلك باستخدام uWSGI، فهو كذلك بايثون التتبع مدمج ويتعلق الأمر فقط بتمكينه في التكوين (الرقم مرفق باسم كل عامل):
py-tracebacker=/var/run/uwsgi/pytrace
بمجرد الانتهاء من ذلك، يمكنك طباعة التتبع الخلفي ببساطة عن طريق الاتصال بالمقبس:
uwsgi --connect-and-read /var/run/uwsgi/pytrace1
أنا في معسكر 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
.