Debugging Python in Production with PyStack
Episode Deep Dive
Guests Introduction and Background
Pablo Galindo Salgado and Matt Wozniski are seasoned Python developers from Bloomberg’s Python Infrastructure team. Pablo is a Python core developer heavily involved with Python releases (release manager for Python 3.10 and 3.11) as well as part of the Python Steering Council. He also contributes to the Faster CPython project, the garbage collector, and other internals of CPython. Matt focuses on developing Python infrastructure tools at Bloomberg, maintaining the interpreter, and moderating the Python Discord community. Together, they co-maintain PyStack, a production debugging tool for Python and C-level code.
What to Know If You're New to Python
Here are a few essentials to help you follow the conversation on debugging:
- Familiarize yourself with Python’s runtime and the idea of stack frames. Even a simple understanding of how Python’s call stacks and exceptions work will help you see what PyStack is capturing.
- Recognize that Python can call (and be called by) external C/C++ modules, meaning Python code often interacts with low-level native code.
- Know that a core dump is a file capturing a program’s memory at the time of a crash. Debuggers and tools like PyStack can read this file to see what happened during a crash.
Key Points and Takeaways
- Why PyStack Exists PyStack addresses the challenge of debugging Python applications that mix Python and native extensions (e.g., C/C++ or Rust). Traditional Python debuggers like
pdb
show only Python-level information, and tools like GDB show only the C call stacks. PyStack combines both, allowing developers to diagnose locked or crashed processes across language boundaries.- Links / Tools:
- Debugging Frozen or Crashed Python Apps A major use case is diagnosing production deadlocks or crashes. When a Python app becomes unresponsive (CPU at 0% or stuck on a deadlock) or abruptly crashes and leaves behind a core dump, PyStack can snapshot the state of each thread and reveal whether the freeze is in Python or in the underlying native code.
- Links / Tools:
- Core dumps documentation (varies by OS /
man core
) - PyStack ReadTheDocs (unofficial user guide if searching online)
- Core dumps documentation (varies by OS /
- Links / Tools:
- Hybrid Stacks: Seeing Python and Native Calls Together Python often calls C/C++ libraries (like NumPy or custom extensions). These libraries might then call back into Python, creating a “Python → C → Python” nested flow. PyStack visualizes this interleaving so you can see exactly how your code entered and exited the native layer.
- Links / Tools:
- NumPy (example of a native-extended library)
- PyStack GitHub issues (for discussion or questions)
- Links / Tools:
- Safe vs. Invasive Debugging Approaches GDB can change process behavior because it can inject code into the running process, but PyStack aims to be a read-only tool. It freezes the process, inspects memory, and resumes execution without altering application state. This makes it safer for use in sensitive or regulated environments where GDB may be prohibited.
- Links / Tools:
- GDB overview
- Bloomberg’s Tech Blog (insights from the maintainers)
- Links / Tools:
- Working with Highly Optimized Python Binaries Many production Python installations lack full debug symbols, making it harder for GDB alone to offer a clear Python stack. PyStack compensates by using heuristics and “forbidden magic” to read memory structures. It also automatically fetches debug info from distribution servers (on many Linux distros), providing inline function details and more clarity without complex user setup.
- Tools / References:
- DebugInfoD servers
- Linux distros’ debug symbol packages
- Tools / References:
- PyStack and Other Profiler Tools (PySpy, Austin) While PyStack and profilers like PySpy or Austin share some underlying techniques, PyStack focuses on correctness over speed and works offline on core dumps. Profilers aim to capture many quick snapshots for performance analysis, but may not fully reconstruct complicated crashed states. PyStack’s purpose is a single, accurate snapshot—even from a dead process.
- Local Variables and Deeper Inspection PyStack can often retrieve local variable values from Python frames (e.g., seeing the contents of lists and dictionaries). It doesn’t call Python’s own
__repr__
—instead, it manually interprets objects in memory. For built-in types likelist
ordict
, it can show the real values, which is extremely helpful when diagnosing weird edge cases or parameter misuse.- Links / Tools:
- CPython internals docs: Dev Guide (peps.python.org/dev/)
- Links / Tools:
- Bloomberg’s Python Ecosystem Both guests highlighted that Bloomberg transitioned from a primarily C++ codebase to a massive amount of Python. Consequently, they created PyStack to handle the challenge of bridging Python with their custom native libraries at scale. This environment ensures that PyStack is tested in complex, real-world scenarios.
- Links / Tools:
- Safe in Production, Yet Great for Testing PyStack can attach to an unresponsive production process without injecting new code, making it a strong choice to keep downtime minimal. It’s also a valuable tool for QA or testing (e.g., via a plugin for pytest) to automatically capture debugging snapshots if a test run deadlocks or crashes.
- Links / Tools:
- Future Enhancements: Subinterpreters, Async, and More With Python 3.12 subinterpreters potentially on the horizon, PyStack may adapt to display each subinterpreter’s state. The maintainers are also exploring supporting asynchronous stacks,
async
/await
flows, and further speed or reliability improvements so that PyStack remains relevant for modern Python challenges.
Interesting Quotes and Stories
- On debugging in production: “It's a bit like being an emergency room doctor—nobody wants to visit one, but they're extremely glad it’s there when needed.”
- On building PyStack: “We say it uses ‘forbidden magic.’ We poke into memory structures that CPython doesn’t officially expose, but that’s how we reconstruct the full picture when everything’s gone wrong.”
Key Definitions and Terms
- Core dump: A snapshot of a program’s memory at the moment it crashes. Used for post-mortem debugging.
- Frame: A call stack element representing a function call (in Python or native code).
- GIL (Global Interpreter Lock): A mutex that prevents multiple native threads in Python from executing Python bytecodes simultaneously.
- Inline Functions: A compiler optimization that replaces a function call with the function’s body to reduce overhead, potentially obscuring standard function frames.
- Heuristics: Techniques PyStack uses to guess memory layouts in the absence of full debugging symbols.
Learning Resources
Here are some courses and materials you might find helpful.
- Python Memory Management and Tips: Deepen your understanding of how memory and references work in Python.
- Getting started with pytest: Explore structured testing in Python, an excellent complement to debugging with tools like PyStack.
- Python for Absolute Beginners: If you are brand-new to Python, this can help you build a solid foundation before diving into advanced debugging.
Overall Takeaway
PyStack stands out as a powerful, safe, and production-ready tool to troubleshoot Python applications that interweave native code and Python logic. From capturing frozen states to analyzing complex crash scenarios with minimal risk, it fills a crucial debugging gap. Whether you’re scaling your apps, exploring concurrency bugs, or hunting elusive memory errors, PyStack offers a compelling blend of “just read memory” accuracy and user-friendly convenience.
Links from the show
Matt Wozniski: github.com
pystack: github.com
Watch this episode on YouTube: youtube.com
Episode transcripts: talkpython.fm
--- Stay in touch with us ---
Subscribe to Talk Python on YouTube: youtube.com
Talk Python on Bluesky: @talkpython.fm at bsky.app
Talk Python on Mastodon: talkpython
Michael on Bluesky: @mkennedy.codes at bsky.app
Michael on Mastodon: mkennedy