يمكنك تغيير تنسيق الملف لإنشاء ملفات تعريف نطاق السرعة أو البيانات الأولية باستخدام المعلمة --format
. راجع py-spy record --help
للحصول على معلومات حول الخيارات الأخرى بما في ذلك تغيير معدل أخذ العينات، والتصفية لتشمل فقط سلاسل الرسائل التي تحتوي على GIL، وتحديد ملفات تعريف ملحقات C الأصلية، وإظهار معرفات سلاسل الرسائل، وإنشاء ملفات تعريف للعمليات الفرعية والمزيد.
يُظهر الجزء العلوي عرضًا مباشرًا للوظائف التي تستغرق معظم الوقت في برنامج python الخاص بك، على غرار أمر Unix top. تشغيل py-spy مع:
py-spy top --pid 12345
# OR
py-spy top -- python myprogram.py
سيعرض عرضًا مباشرًا عالي المستوى للتحديث لبرنامج python الخاص بك:
يمكن لـ py-spy أيضًا عرض مكدس الاستدعاءات الحالي لكل مؤشر ترابط بيثون باستخدام أمر dump
:
py-spy dump --pid 12345
سيؤدي هذا إلى تفريغ مكدسات الاستدعاءات لكل مؤشر ترابط وبعض معلومات العملية الأساسية الأخرى إلى وحدة التحكم:
يعد هذا مفيدًا في الحالة التي تحتاج فيها فقط إلى مكدس استدعاءات واحد لمعرفة مكان تعليق برنامج بايثون الخاص بك. يتمتع هذا الأمر أيضًا بالقدرة على طباعة المتغيرات المحلية المرتبطة بكل إطار مكدس عن طريق تعيين علامة --locals
.
يهدف هذا المشروع إلى السماح لك بإنشاء ملف تعريف وتصحيح أي برنامج بايثون قيد التشغيل، حتى لو كان البرنامج يخدم حركة الإنتاج.
على الرغم من وجود العديد من مشاريع ملفات تعريف Python الأخرى، إلا أن جميعها تقريبًا تتطلب تعديل البرنامج المُصمم بطريقة ما. عادة، يتم تشغيل رمز ملف التعريف داخل عملية بايثون المستهدفة، مما سيؤدي إلى إبطاء وتغيير كيفية عمل البرنامج. وهذا يعني أنه ليس من الآمن عمومًا استخدام ملفات التعريف هذه لتصحيح الأخطاء في خدمات الإنتاج نظرًا لأنها عادةً ما يكون لها تأثير ملحوظ على الأداء.
يعمل py-spy عن طريق قراءة ذاكرة برنامج python مباشرة باستخدام استدعاء نظامprocess_vm_readv على Linux، أو استدعاء vm_read على OSX أو استدعاء ReadProcessMemory على Windows.
يتم التعرف على مكدس الاستدعاءات لبرنامج Python من خلال النظر إلى متغير PyInterpreterState العام لتشغيل جميع سلاسل Python في المترجم، ثم التكرار على كل PyFrameObject في كل موضوع للحصول على مكدس الاستدعاءات. نظرًا لأن Python ABI يتغير بين الإصدارات، فإننا نستخدم Rust's Bindgen لإنشاء هياكل مختلفة لكل فئة مترجم Python التي نهتم بها ونستخدم هذه الهياكل التي تم إنشاؤها لمعرفة تخطيط الذاكرة في برنامج Python.
يمكن أن يكون الحصول على عنوان الذاكرة الخاص بمترجم Python أمرًا صعبًا بعض الشيء بسبب التوزيع العشوائي لتخطيط مساحة العنوان. إذا كان مترجم بايثون الهدف يأتي مع رموز، فمن السهل جدًا معرفة عنوان ذاكرة المترجم عن طريق إلغاء الإشارة إلى متغيرات interp_head
أو _PyRuntime
اعتمادًا على إصدار بايثون. ومع ذلك، يتم شحن العديد من إصدارات Python إما مع ثنائيات مجردة أو يتم شحنها بدون ملفات رموز PDB المقابلة على نظام التشغيل Windows. في هذه الحالات، نقوم بفحص قسم BSS بحثًا عن العناوين التي تبدو وكأنها قد تشير إلى PyInterpreterState صالح ونتحقق مما إذا كان تخطيط هذا العنوان هو ما نتوقعه.
نعم! يدعم py-spy إنشاء ملفات تعريف لامتدادات python الأصلية المكتوبة بلغات مثل C/C++ أو Cython، على نظام التشغيل x86_64 Linux وWindows. يمكنك تمكين هذا الوضع عن طريق تمرير --native
في سطر الأوامر. للحصول على أفضل النتائج، يجب عليك تجميع ملحق بايثون الخاص بك باستخدام الرموز. ومن الجدير بالذكر أيضًا بالنسبة لبرامج Cython أن py-spy يحتاج إلى ملف C أو C++ الذي تم إنشاؤه من أجل إرجاع أرقام الأسطر لملف .pyx الأصلي. اقرأ منشور المدونة لمزيد من المعلومات.
من خلال تمرير علامة --subprocesses
إما إلى السجل أو العرض العلوي، سيتضمن py-spy أيضًا الإخراج من أي عملية بايثون تكون عملية فرعية للبرنامج المستهدف. يعد هذا مفيدًا في إنشاء ملفات تعريف للتطبيقات التي تستخدم مجموعات عاملة متعددة المعالجة أو gunicorn. سيقوم py-spy بمراقبة العمليات الجديدة التي يتم إنشاؤها، وإرفاقها تلقائيًا وتضمين عينات منها في الإخراج. سيتضمن عرض السجل معرف PID وخط cmdline لكل برنامج في مكدس الاستدعاءات، مع ظهور العمليات الفرعية كأبناء للعمليات الأصلية الخاصة بها.
يعمل py-spy عن طريق قراءة الذاكرة من عملية بايثون مختلفة، وقد لا يكون هذا مسموحًا به لأسباب أمنية اعتمادًا على نظام التشغيل وإعدادات النظام لديك. في كثير من الحالات، يؤدي التشغيل كمستخدم جذر (باستخدام sudo أو ما شابه) إلى تجاوز قيود الأمان هذه. يتطلب OSX دائمًا التشغيل كجذر، ولكن في Linux يعتمد الأمر على كيفية تشغيل py-spy وإعدادات أمان النظام.
في نظام التشغيل Linux، يكون التكوين الافتراضي هو طلب أذونات الجذر عند إرفاق عملية ليست تابعة. بالنسبة لـ py-spy، هذا يعني أنه يمكنك الملف الشخصي دون الوصول إلى الجذر عن طريق الحصول على py-spy لإنشاء العملية ( py-spy record -- python myprogram.py
) ولكن إرفاق عملية موجودة عن طريق تحديد PID سيتطلب عادةً الجذر ( sudo py-spy record --pid 123456
). يمكنك إزالة هذا القيد على Linux عن طريق تعيين المتغير ptrace_scope sysctl.
يحاول py-spy تضمين تتبعات المكدس فقط من سلاسل الرسائل التي تقوم بتشغيل التعليمات البرمجية بشكل نشط، واستبعاد سلاسل الرسائل التي تكون في وضع السكون أو الخمول. عندما يكون ذلك ممكنًا، يحاول py-spy الحصول على معلومات نشاط مؤشر الترابط هذا من نظام التشغيل: من خلال القراءة في /proc/PID/stat
على Linux، باستخدام استدعاء mach thread_basic_info على OSX، ومن خلال البحث عما إذا كان SysCall الحالي معروفًا بأنه خامل على ويندوز.
هناك بعض القيود في هذا الأسلوب على الرغم من أن ذلك قد يتسبب في استمرار وضع علامة على سلاسل الرسائل الخاملة على أنها نشطة. أولاً، علينا الحصول على معلومات نشاط الموضوع قبل إيقاف البرنامج مؤقتًا، لأن الحصول على هذه المعلومات من برنامج متوقف مؤقتًا سيؤدي دائمًا إلى إرجاع هذا إلى أنه خامل. هذا يعني أن هناك حالة سباق محتملة، حيث نحصل على نشاط الخيط ثم يكون الخيط في حالة مختلفة عندما نحصل على تتبع المكدس. لم يتم أيضًا تنفيذ الاستعلام عن نظام التشغيل لنشاط مؤشر الترابط حتى الآن لمعالجات FreeBSD وi686/ARM على Linux. في نظام التشغيل Windows، لن يتم أيضًا وضع علامة على المكالمات المحظورة على الإدخال والإخراج على أنها خاملة حتى الآن، على سبيل المثال عند قراءة الإدخال من stdin. أخيرًا، في بعض استدعاءات Linux، قد يتسبب إرفاق ptrace الذي نستخدمه في استيقاظ سلاسل الرسائل الخاملة للحظات، مما يتسبب في نتائج إيجابية خاطئة عند القراءة من procfs. لهذه الأسباب، لدينا أيضًا احتياطي إرشادي يحدد بعض الاستدعاءات المعروفة في لغة بايثون على أنها خاملة.
يمكنك تعطيل هذه الوظيفة عن طريق تعيين علامة --idle
، والتي ستتضمن الإطارات التي يعتبرها py-spy خاملة.
نحصل على نشاط GIL من خلال النظر إلى قيمة مؤشر الترابط المشار إليها بواسطة رمز _PyThreadState_Current
لـ Python 3.6 والإصدارات الأقدم ومن خلال معرفة المكافئ من بنية _PyRuntime
في Python 3.7 والإصدارات الأحدث. قد لا يتم تضمين هذه الرموز في توزيع بايثون الخاص بك، مما سيؤدي إلى فشل تحديد الخيط الذي يحتفظ بـ GIL. يظهر استخدام GIL الحالي أيضًا في العرض top
كـ %GIL.
سيؤدي تمرير علامة --gil
إلى تضمين فقط آثار سلاسل الرسائل التي تمسك بقفل المترجم العالمي. في بعض الحالات، قد يكون هذا عرضًا أكثر دقة لكيفية قضاء برنامج بايثون لوقته، على الرغم من أنه يجب أن تدرك أن هذا سيفقد النشاط في الامتدادات التي تطلق GIL بينما لا يزال نشطًا.
يحتوي OSX على ميزة تسمى حماية تكامل النظام والتي تمنع حتى المستخدم الجذر من قراءة الذاكرة من أي ثنائي موجود في /usr/bin. ولسوء الحظ، يتضمن هذا مترجم بايثون الذي يأتي مع OSX.
هناك طريقتان مختلفتان للتعامل مع هذا:
عادةً ما يؤدي تشغيل py-spy داخل حاوية عامل الإرساء إلى ظهور خطأ رفض الأذونات حتى عند التشغيل كجذر.
يحدث هذا الخطأ بسبب قيام عامل الإرساء بتقييد استدعاء نظامprocess_vm_readv الذي نستخدمه. يمكن تجاوز ذلك عن طريق إعداد --cap-add SYS_PTRACE
عند بدء تشغيل حاوية عامل الإرساء.
وبدلاً من ذلك، يمكنك تحرير ملف yaml الخاص بتكوين عامل الإرساء
your_service:
cap_add:
- SYS_PTRACE
لاحظ أنك ستحتاج إلى إعادة تشغيل حاوية عامل الإرساء حتى يدخل هذا الإعداد حيز التنفيذ.
يمكنك أيضًا استخدام py-spy من نظام التشغيل Host لتعريف عملية قيد التشغيل داخل حاوية عامل الإرساء.
يحتاج py-spy SYS_PTRACE
ليتمكن من قراءة ذاكرة العملية. يقوم Kubernetes بإسقاط هذه الإمكانية بشكل افتراضي، مما يؤدي إلى حدوث الخطأ
Permission Denied: Try running again with elevated permissions by going 'sudo env "PATH=$PATH" !!'
الطريقة الموصى بها للتعامل مع هذا هي تعديل المواصفات وإضافة تلك الإمكانية. بالنسبة للنشر، يتم ذلك عن طريق إضافة هذا إلى Deployment.spec.template.spec.containers
securityContext:
capabilities:
add:
- SYS_PTRACE
مزيد من التفاصيل حول هذا هنا: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capabilities-for-a-container لاحظ أن هذا سيؤدي إلى إزالة القرون الموجودة وإنشاءها مرة أخرى .
تختار Alpine python الخروج من manylinux
عجلات Linux: pypa/pip#3969 (تعليق). يمكنك تجاوز هذا السلوك لاستخدام النقطة لتثبيت py-spy على Alpine من خلال الانتقال إلى:
echo 'manylinux1_compatible = True' > /usr/local/lib/python3.7/site-packages/_manylinux.py
وبدلاً من ذلك، يمكنك تنزيل ملف ثنائي musl من صفحة إصدارات GitHub.
من خلال تعيين خيار --nonblocking
، لن يقوم py-spy بإيقاف الثعبان المستهدف الذي تقوم بالتوصيف منه مؤقتًا. في حين أن تأثير الأداء لأخذ العينات من عملية باستخدام py-spy عادة ما يكون منخفضًا للغاية، فإن تعيين هذا الخيار سوف يتجنب تمامًا مقاطعة برنامج بايثون قيد التشغيل.
مع تعيين هذا الخيار، سيقوم py-spy بدلاً من ذلك بقراءة حالة المترجم الفوري من عملية بايثون أثناء تشغيلها. نظرًا لأن الاستدعاءات التي نستخدمها لقراءة الذاكرة ليست ذرية، وعلينا إصدار مكالمات متعددة للحصول على تتبع المكدس، فهذا يعني أننا نتلقى أحيانًا أخطاء عند أخذ العينات. يمكن أن يظهر هذا كمعدل خطأ متزايد عند أخذ العينات، أو كإطارات مكدسة جزئية يتم تضمينها في الإخراج.
ليس بعد =).
إذا كانت هناك ميزات ترغب في رؤيتها في py-spy، فإما أن تقوم بإبهام المشكلة المناسبة أو إنشاء مشكلة جديدة تصف الوظائف المفقودة.
يتبع py-spy مواصفات CLICOLOR، وبالتالي فإن تعيين CLICOLOR_FORCE=1
في بيئتك سيكون له مخرجات طباعة ملونة py-spy حتى عند إرساله إلى جهاز النداء.
py-spy مستوحى بشكل كبير من عمل جوليا إيفانز الممتاز في rbspy. على وجه الخصوص، يتم أخذ التعليمات البرمجية لإنشاء ملفات Flamegraph وSpeedscope مباشرة من rbspy، ويستخدم هذا المشروع ذاكرة عملية القراءة وصناديق خرائط proc التي تم فصلها عن rbspy.
تم إصدار py-spy بموجب ترخيص MIT، راجع ملف الترخيص للحصول على النص الكامل.