Python’s built-in pdb debugger is usually started before a program runs, either with python -m pdb script.py or by placing breakpoint() in the source code. Recent Python versions also support attaching the debugger to an already running Python process with the -p option. This is useful when a service, worker, automation, or long-running script hangs, loops, consumes unexpected CPU, or stops responding without exiting.
What attaching a debugger means
In a normal debugging session, you reproduce a problem under controlled conditions. With process attachment, you inspect the real process that is currently showing the problem. You identify its process ID, run the debugger against that PID, and examine the live call stack and objects.
This can dramatically reduce diagnostic time for intermittent failures. Problems that appear only after hours of work, a specific queue item, a rare race condition, or an unusual network response may be difficult to recreate. Attaching lets you observe the current state before it disappears.
Finding the correct process ID
The PID is the operating system’s numeric identifier for a process. On Linux and macOS, commands such as ps, pgrep, and top can help. On Windows, use Task Manager, PowerShell, or your monitoring platform.
ps aux | grep python
pgrep -af pythonDo not select a process only because its command contains “python.” Confirm the application path, user, arguments, parent process, host, and start time. Production systems often run many similar workers, and attaching to the wrong one can pause unrelated work.
Basic pdb -p command
After confirming the PID, the general command is:
python -m pdb -p 12345Replace 12345 with the real PID. The attaching interpreter should normally match the environment and version of the target process. Operating system permissions may require the same user account or authorized elevated privileges.
Essential pdb commands
Inside the debugger, where or w prints the call stack. up and down move between frames. list displays nearby source lines. p expression evaluates an expression, while pp expression prints complex values more clearly.
(Pdb) where
(Pdb) up
(Pdb) pp state
(Pdb) p len(queue)
(Pdb) listExpression evaluation is not automatically safe. Properties, custom representations, and function calls can execute code or modify state. Prefer simple reads of local variables, lengths, flags, and immutable values.
Diagnosing CPU loops
When a process consumes a full CPU core, the current stack often reveals the loop. Inspect the condition that should terminate it, the input being processed, retry counters, and timestamps. Move upward until you reach application code rather than framework internals.
A loop may be logically infinite, or it may be repeatedly handling the same failing input. Check whether an index advances, whether a queue item is acknowledged, whether a timeout uses the correct unit, and whether an exception is swallowed and retried without delay.
Diagnosing waits and hangs
A process that looks frozen may be waiting for a socket, database query, file operation, subprocess, lock, event, or queue. The stack helps distinguish a legitimate wait from a deadlock. Look for synchronization calls and identify the object involved.
For related advanced topics, see our guides to queue.ShutDown, sys._is_gil_enabled, os.timerfd_create, and contextlib.chdir.
Threads and asynchronous applications
Threaded and asynchronous programs require extra care. The attached frame may not be the one responsible for the visible symptom. A request handler could be blocked while another thread owns a lock. An async task could be waiting for a future that is never completed.
Use the debugger together with thread dumps, task inspection, structured logs, and metrics. Avoid assuming that the first visible frame is the root cause. Trace resource ownership and dependencies between tasks.
Permissions and platform restrictions
Process attachment depends on operating system facilities. Linux security settings related to ptrace, container namespaces, capabilities, hardened kernels, and managed hosting policies may block access. Windows and macOS have their own permission models and implementation limitations.
Do not bypass controls without authorization. Debugging another process can expose its memory and credentials. In regulated or multi-tenant environments, use an approved incident procedure and document who accessed the process.
Security and privacy risks
A debugger can display API keys, passwords, cookies, personal data, database records, and internal configuration. Never paste raw debugger output into public issues or unrestricted chat channels. Collect only what is necessary, redact sensitive values, and close the session promptly.
Attaching may pause or slow the target. On a production service, remove an instance from the load balancer or redirect traffic before debugging when possible. Confirm recovery after leaving the debugger.
pdb versus logging and observability
Logs record events selected before the incident. Metrics summarize behavior over time. Traces connect operations across components. A live debugger exposes the state that exists now, including values that were never logged.
These tools should complement each other. Use pdb -p as a targeted diagnostic technique, not as a replacement for timeouts, health checks, structured logging, tracing, and alerts.
Preparing code for live diagnosis
Small functions, descriptive names, explicit state, and clear boundaries make stack inspection easier. Separate validation, transformation, and side effects. Avoid huge expressions that hide several operations on one line.
Add correlation IDs, bounded retries, timeouts, and meaningful exception messages. The goal is to make most incidents diagnosable from observability data and reserve live attachment for difficult cases.
A safe operational workflow
- Confirm the incident, host, start time, and symptoms.
- Identify the correct PID and process owner.
- Check whether a replica or lower-risk environment is available.
- Attach with
python -m pdb -p PID. - Inspect the stack and values without modifying state.
- Record only necessary, non-sensitive findings.
- Exit and verify process health.
- Turn the finding into a fix, regression test, and observability improvement.
Compatibility and testing
Because process attachment is recent and platform-dependent, confirm support in the installed Python build. Review the official pdb documentation and the Python command-line documentation. Practice on a disposable process before using it during a real incident.
Common mistakes
Common mistakes include using the wrong Python environment, choosing the wrong PID, evaluating expressions with side effects, leaving the process paused, exposing secrets in screenshots, and treating a single frame as proof of the root cause. Another mistake is fixing the immediate process manually without creating a code-level correction.
Conclusion
pdb -p provides a direct way to inspect a running Python process when restarting or reproducing the issue would destroy valuable evidence. It can reveal loops, blocking calls, deadlocks, unexpected inputs, and broken state. Used carefully, with proper authorization and operational safeguards, it is a powerful addition to a mature Python debugging workflow.







