คุณสามารถเปลี่ยนรูปแบบไฟล์เพื่อสร้างโปรไฟล์ speedscope หรือข้อมูลดิบด้วยพารามิเตอร์ --format
ดู py-spy record --help
สำหรับข้อมูลเกี่ยวกับตัวเลือกอื่นๆ รวมถึงการเปลี่ยนอัตราการสุ่มตัวอย่าง การกรองเพื่อรวมเฉพาะเธรดที่เก็บ GIL การทำโปรไฟล์ส่วนขยาย C ดั้งเดิม การแสดง thread-ids กระบวนการย่อยการทำโปรไฟล์ และอื่นๆ
Top จะแสดงมุมมองสดว่าฟังก์ชันใดใช้เวลามากที่สุดในโปรแกรม Python ของคุณ ซึ่งคล้ายกับคำสั่ง Unix top ใช้ py-spy ด้วย:
py-spy top --pid 12345
# OR
py-spy top -- python myprogram.py
จะแสดงมุมมองระดับสูงที่อัปเดตสดของโปรแกรม python ของคุณ:
py-spy ยังสามารถแสดง call stack ปัจจุบันสำหรับแต่ละเธรด python ด้วยคำสั่ง dump
:
py-spy dump --pid 12345
การดำเนินการนี้จะดัมพ์การเรียกสแต็กสำหรับแต่ละเธรด และข้อมูลกระบวนการพื้นฐานอื่น ๆ ไปยังคอนโซล:
สิ่งนี้มีประโยชน์สำหรับกรณีที่คุณต้องการ call stack เดียวเพื่อดูว่าโปรแกรม python ของคุณค้างอยู่ที่ใด คำสั่งนี้ยังมีความสามารถในการพิมพ์ตัวแปรโลคัลที่เกี่ยวข้องกับแต่ละเฟรมสแต็กโดยการตั้งค่าแฟล็ก --locals
โปรเจ็กต์นี้มีจุดมุ่งหมายเพื่อให้คุณสร้างโปรไฟล์และแก้ไขข้อบกพร่องของโปรแกรม Python ใดๆ ที่ทำงานอยู่ได้ แม้ว่าโปรแกรมนั้นจะรองรับปริมาณการใช้งานจริงก็ตาม
แม้ว่าจะมีโปรเจ็กต์การทำโปรไฟล์ Python อื่นๆ อีกมากมาย แต่เกือบทั้งหมดจำเป็นต้องแก้ไขโปรแกรมที่ทำโปรไฟล์ไม่ทางใดก็ทางหนึ่ง โดยปกติแล้ว โค้ดโปรไฟล์จะทำงานภายในกระบวนการ Python เป้าหมาย ซึ่งจะทำให้ช้าลงและเปลี่ยนวิธีการทำงานของโปรแกรม ซึ่งหมายความว่า โดยทั่วไปแล้วจะไม่ปลอดภัยที่จะใช้ตัวสร้างโปรไฟล์เหล่านี้เพื่อแก้ไขปัญหาในบริการการผลิต เนื่องจากโดยปกติแล้วจะมีผลกระทบต่อประสิทธิภาพที่เห็นได้ชัดเจน
py-spy ทำงานโดยการอ่านหน่วยความจำของโปรแกรม python โดยตรงโดยใช้การเรียกระบบ process_vm_readv บน Linux, การเรียก vm_read บน OSX หรือการเรียก ReadProcessMemory บน Windows
การระบุ call stack ของโปรแกรม Python ทำได้โดยการดูที่ตัวแปร PyInterpreterState โกลบอลเพื่อให้เธรด Python ทั้งหมดทำงานในล่าม จากนั้นวนซ้ำ PyFrameObject แต่ละตัวในแต่ละเธรดเพื่อรับ call stack เนื่องจาก Python ABI เปลี่ยนแปลงระหว่างเวอร์ชัน เราจึงใช้ตัวประสานของสนิมเพื่อสร้างโครงสร้างสนิมที่แตกต่างกันสำหรับคลาสล่าม Python แต่ละคลาสที่เราสนใจ และใช้โครงสร้างที่สร้างขึ้นเหล่านี้เพื่อค้นหาเค้าโครงหน่วยความจำในโปรแกรม Python
การรับที่อยู่หน่วยความจำของ Python Interpreter อาจยุ่งยากเล็กน้อยเนื่องจากการสุ่มเค้าโครงพื้นที่ที่อยู่ หากล่ามหลามเป้าหมายมาพร้อมกับสัญลักษณ์ มันค่อนข้างง่ายที่จะค้นหาที่อยู่หน่วยความจำของล่ามโดยการยกเลิกการอ้างอิงตัวแปร interp_head
หรือ _PyRuntime
ขึ้นอยู่กับเวอร์ชันของ Python อย่างไรก็ตาม Python หลายเวอร์ชันจัดส่งพร้อมกับไบนารีแบบแยกส่วนหรือจัดส่งโดยไม่มีไฟล์สัญลักษณ์ PDB ที่เกี่ยวข้องบน Windows ในกรณีเหล่านี้ เราจะสแกนส่วน BSS เพื่อหาที่อยู่ที่ดูเหมือนว่าอาจชี้ไปที่ PyInterpreterState ที่ถูกต้อง และตรวจสอบว่ารูปแบบของที่อยู่นั้นเป็นไปตามที่เราคาดหวังหรือไม่
ใช่! py-spy รองรับการสร้างโปรไฟล์ส่วนขยายของ Python ดั้งเดิมที่เขียนในภาษาเช่น C/C++ หรือ Cython บน x86_64 Linux และ Windows คุณสามารถเปิดใช้งานโหมดนี้ได้โดยส่ง --native
บนบรรทัดคำสั่ง เพื่อให้ได้ผลลัพธ์ที่ดีที่สุด คุณควรคอมไพล์ส่วนขยาย Python ของคุณด้วยสัญลักษณ์ สิ่งที่น่าสังเกตสำหรับโปรแกรม Cython ก็คือ py-spy ต้องการไฟล์ C หรือ C++ ที่สร้างขึ้นเพื่อส่งคืนหมายเลขบรรทัดของไฟล์ .pyx ดั้งเดิม อ่านโพสต์บล็อกสำหรับข้อมูลเพิ่มเติม
โดยการส่งแฟล็ก --subprocesses
ไปยังเรคคอร์ดหรือมุมมองด้านบน py-spy จะรวมเอาต์พุตจากกระบวนการ python ใด ๆ ที่เป็นกระบวนการลูกของโปรแกรมเป้าหมายด้วย สิ่งนี้มีประโยชน์สำหรับแอปพลิเคชันการทำโปรไฟล์ที่ใช้มัลติโพรเซสซิงหรือพูลผู้ปฏิบัติงาน gunicorn py-spy จะตรวจสอบกระบวนการใหม่ที่กำลังถูกสร้างขึ้น และแนบไปกับกระบวนการเหล่านั้นโดยอัตโนมัติและรวมตัวอย่างจากกระบวนการเหล่านั้นในเอาต์พุต มุมมองบันทึกจะรวม PID และ cmdline ของแต่ละโปรแกรมใน callstack โดยที่กระบวนการย่อยปรากฏเป็นลูกของกระบวนการหลัก
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 ปัจจุบันเป็นที่รู้จักว่าไม่ได้ใช้งานหรือไม่ บน Windows
มีข้อจำกัดบางประการเกี่ยวกับวิธีการนี้ แต่อาจทำให้เธรดที่ไม่ได้ใช้งานยังคงถูกทำเครื่องหมายว่าใช้งานอยู่ ก่อนอื่น เราต้องรับข้อมูลกิจกรรมของเธรดนี้ก่อนที่จะหยุดโปรแกรมชั่วคราว เนื่องจากการได้รับสิ่งนี้จากโปรแกรมที่หยุดชั่วคราวจะทำให้โปรแกรมส่งคืนเสมอว่าไม่ได้ใช้งาน ซึ่งหมายความว่ามีสภาวะการแข่งขันที่อาจเกิดขึ้น โดยที่เราได้รับกิจกรรมของเธรด จากนั้นเธรดจะอยู่ในสถานะอื่นเมื่อเราได้รับการติดตามสแต็ก การสืบค้นระบบปฏิบัติการสำหรับกิจกรรมเธรดยังไม่ได้ถูกนำมาใช้กับโปรเซสเซอร์ FreeBSD และ i686/ARM บน Linux บน Windows การโทรที่ถูกบล็อกบน IO จะยังไม่ถูกทำเครื่องหมายว่าไม่ได้ใช้งาน เช่น เมื่ออ่านอินพุตจาก stdin สุดท้ายนี้ บน Linux บางรุ่นการเรียกไฟล์แนบ ptrace ที่เราใช้อยู่อาจทำให้เธรดที่ไม่ได้ใช้งานตื่นขึ้นชั่วขณะ ทำให้เกิดผลบวกลวงเมื่ออ่านจาก procfs ด้วยเหตุผลเหล่านี้ เรายังมีทางเลือกแบบฮิวริสติกที่ทำเครื่องหมายว่าการเรียกที่รู้จักใน python เป็นการไม่ได้ใช้งาน
คุณสามารถปิดการใช้งานฟังก์ชันนี้ได้โดยการตั้งค่า --idle
flag ซึ่งจะรวมเฟรมที่ py-spy พิจารณาว่าไม่ได้ใช้งาน
เราได้รับกิจกรรม GIL โดยดูที่ค่า threadid ที่ชี้โดยสัญลักษณ์ _PyThreadState_Current
สำหรับ Python 3.6 และรุ่นก่อนหน้า และโดยการหาค่าที่เทียบเท่าจากโครงสร้าง _PyRuntime
ใน Python 3.7 และใหม่กว่า สัญลักษณ์เหล่านี้อาจไม่รวมอยู่ในการกระจายหลามของคุณ ซึ่งจะทำให้การแก้ไขเธรดที่ยึด GIL ล้มเหลว การใช้งาน GIL ปัจจุบันจะแสดงในมุมมอง top
เป็น %GIL
การส่งผ่านแฟล็ก --gil
จะรวมเฉพาะการติดตามเธรดที่ยึด Global Interpreter Lock เท่านั้น ในบางกรณี นี่อาจเป็นมุมมองที่แม่นยำยิ่งขึ้นว่าโปรแกรม python ของคุณใช้เวลาอย่างไร แม้ว่าคุณควรตระหนักว่าการดำเนินการนี้จะพลาดกิจกรรมในส่วนขยายที่เผยแพร่ GIL ในขณะที่ยังคงทำงานอยู่
OSX มีคุณลักษณะที่เรียกว่า System Integrity Protection ซึ่งป้องกันไม่ให้แม้แต่ผู้ใช้รูทอ่านหน่วยความจำจากไบนารีใดๆ ที่อยู่ใน /usr/bin น่าเสียดายที่นี่รวมล่ามหลามที่มาพร้อมกับ OSX ด้วย
มีสองวิธีในการจัดการกับสิ่งนี้:
การเรียกใช้ py-spy ภายในคอนเทนเนอร์นักเทียบท่ามักจะทำให้เกิดข้อผิดพลาดในการอนุญาตที่ถูกปฏิเสธแม้ว่าจะทำงานในฐานะรูทก็ตาม
ข้อผิดพลาดนี้เกิดจากการที่นักเทียบท่าจำกัดการเรียกระบบ process_vm_readv ที่เราใช้อยู่ สิ่งนี้สามารถแทนที่ได้โดยการตั้งค่า --cap-add SYS_PTRACE
เมื่อเริ่มต้นคอนเทนเนอร์นักเทียบท่า
หรือคุณสามารถแก้ไขไฟล์ yaml ที่เขียนโดยนักเทียบท่าได้
your_service:
cap_add:
- SYS_PTRACE
โปรดทราบว่าคุณจะต้องรีสตาร์ทคอนเทนเนอร์นักเทียบท่าเพื่อให้การตั้งค่านี้มีผล
คุณยังสามารถใช้ py-spy จาก Host OS เพื่อสร้างโปรไฟล์กระบวนการทำงานที่ทำงานอยู่ภายในคอนเทนเนอร์นักเทียบท่า
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
: pypa/pip#3969 (ความคิดเห็น) คุณสามารถแทนที่พฤติกรรมนี้เพื่อใช้ pip เพื่อติดตั้ง py-spy บน Alpine ได้โดยไปที่:
echo 'manylinux1_compatible = True' > /usr/local/lib/python3.7/site-packages/_manylinux.py
หรือคุณสามารถดาวน์โหลด musl binary ได้จากหน้าเผยแพร่ GitHub
ด้วยการตั้งค่าตัวเลือก --nonblocking
py-spy จะไม่หยุดหลามเป้าหมายที่คุณกำลังสร้างโปรไฟล์อยู่ชั่วคราว แม้ว่าผลกระทบต่อประสิทธิภาพของการสุ่มตัวอย่างจากกระบวนการด้วย py-spy มักจะต่ำมาก การตั้งค่าตัวเลือกนี้จะหลีกเลี่ยงการรบกวนโปรแกรม python ที่กำลังรันอยู่โดยสิ้นเชิง
ด้วยชุดตัวเลือกนี้ py-spy จะอ่านสถานะล่ามจากกระบวนการหลามในขณะที่กำลังทำงานอยู่แทน เนื่องจากการเรียกที่เราใช้เพื่ออ่านหน่วยความจำไม่ใช่อะตอมมิก และเราต้องออกการเรียกหลายครั้งเพื่อรับการติดตามสแต็ก ซึ่งหมายความว่าบางครั้งเราได้รับข้อผิดพลาดเมื่อสุ่มตัวอย่าง ซึ่งอาจแสดงเป็นอัตราข้อผิดพลาดที่เพิ่มขึ้นเมื่อสุ่มตัวอย่าง หรือแสดงเป็นเฟรมสแต็กบางส่วนในเอาต์พุต
ยังไม่มี =)
หากมีฟีเจอร์ที่คุณต้องการเห็นใน py-spy ให้ยกนิ้วให้ปัญหาที่เหมาะสมหรือสร้างฟีเจอร์ใหม่ที่อธิบายว่าฟังก์ชันใดที่ขาดหายไป
py-spy เป็นไปตามข้อกำหนดของ CLICOLOR ดังนั้นการตั้งค่า CLICOLOR_FORCE=1
ในสภาพแวดล้อมของคุณจะมีเอาต์พุตสีที่พิมพ์ด้วย py-spy แม้ว่าจะถูกส่งไปยังเพจเจอร์ก็ตาม
py-spy ได้รับแรงบันดาลใจอย่างมากจากงานที่ยอดเยี่ยมของ Julia Evans ใน rbspy โดยเฉพาะอย่างยิ่ง โค้ดสำหรับสร้างไฟล์ Flamegraph และ Speedscope จะถูกดึงมาจาก rbspy โดยตรง และโปรเจ็กต์นี้ใช้กล่อง read-process-memory และ proc-maps ที่แยกออกมาจาก rbspy
py-spy ได้รับการเผยแพร่ภายใต้ใบอนุญาต MIT โปรดดูไฟล์ใบอนุญาตสำหรับข้อความฉบับเต็ม