码农高天
AI-assisted summary of 我在PyCon US上的Talk,pdb的基本机制 — original source. Check the original for full context.
Source published:
Key ideas
- Python 标准库调试器 pdb 在底层依赖 sys.settrace() 机制,但由于其必须全局分发 call 事件且只能函数级过滤 line 事件,会导致测试中递归代码(如 fib(24))出现高达 100 到 200 倍的严重性能衰减。 (15:00)
- pdb 暂停程序和单步调试并无特殊硬件级黑魔法,本质是在 trace 回调中直接运行类似 REPL 的交互式 input 死循环,并通过对下一行代码事件的比对来决定是否停靠。 (6:00)
- Python 3.12 引入的 PEP 669(sys.monitoring)通过以代码对象(code object)为粒度的局部监控与动态关闭非断点代码行事件,成功打破了全局追踪瓶颈,为调试器带来近零开销运行的可能。 (15:00)
- 尽管 sys.monitoring 因需要主动编排事件和保障向下兼容而在标准库 pdb 中尚未正式合入,但第三方调试工具如 PyCharm (pydevd)、VS Code (debugpy) 以及 coverage.py 均已跟进集成该机制以实现百倍提速。 (20:00)
Detailed notes
## Background 该视频由 YouTube 频道“码农高天”于 2024 年 8 月 27 日发布。内容为其在 PyCon US 2024 上发表的附带双语字幕的技术演讲,题为《What makes a Python debugger possible and how can we make it 100x faster》,旨在向开发者揭秘 pdb 背后的底层工作原理及将调试器速度提升百倍的优化方案。
## TL;DR 本场演讲深入剖析了 Python 进程内调试器的底层实现机理与演进方向。高天首先讲解了 pdb 如何利用 `sys.settrace()` 捕获全局调用与代码行事件,并通过回溯栈帧修补 `f_trace` 以及启动类 REPL 交互循环来挂起程序和检查局部变量。随后,他通过基准测试指出传统跟踪因粗粒度事件派发导致程序面临上百倍性能衰减的致命缺陷。最后,他详细介绍了 Python 3.12 引入的 `sys.monitoring`(PEP 669)机制,展示了如何通过按代码对象按需监听和动态禁用非断点行事件来实现近乎零开销的高性能调试,并展望了该特性在 pydevd、debugpy 等生态工具中的落地应用。
## Atmosphere & style 整场演讲呈现出轻松风趣却极具极客深度的硬核技术分享氛围。高天以幽默的现场举手互动破冰,随后迅速转入底层系统机制,层层剥开看似神秘的调试器“魔法”,用极为平实的逻辑还原 CPython 栈帧与解释器事件派发的本质。讲解节奏严谨利落、直击痛点,既坦率指出了历史遗留设计的性能局限,又洋溢着对 Python 现代监控架构演进的工程热情。
## Bulletin - [15:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=900s) Python 标准库调试器 pdb 在底层依赖 sys.settrace() 机制,但由于其必须全局分发 call 事件且只能函数级过滤 line 事件,会导致测试中递归代码(如 fib(24))出现高达 100 到 200 倍的严重性能衰减。 - [06:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=360s) pdb 暂停程序和单步调试并无特殊硬件级黑魔法,本质是在 trace 回调中直接运行类似 REPL 的交互式 input 死循环,并通过对下一行代码事件的比对来决定是否停靠。 - [15:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=900s) Python 3.12 引入的 PEP 669(sys.monitoring)通过以代码对象(code object)为粒度的局部监控与动态关闭非断点代码行事件,成功打破了全局追踪瓶颈,为调试器带来近零开销运行的可能。 - [20:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=1200s) 尽管 sys.monitoring 因需要主动编排事件和保障向下兼容而在标准库 pdb 中尚未正式合入,但第三方调试工具如 PyCharm (pydevd)、VS Code (debugpy) 以及 coverage.py 均已跟进集成该机制以实现百倍提速。
## Key takeaways - [00:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=0s) 进程内调试器(in-process debugger)的核心能力可归纳为任意位置暂停、获取运行时上下文、动态执行求值以及细粒度控制程序执行流四项要求。 - [02:30](https://www.youtube.com/watch?v=kzTDEaQx0us&t=150s) sys.settrace() 是 Python 原生追踪机制的核心,通过注册全局回调监听 call、return、line 和 exception 四类解释器内部事件。 - [02:30](https://www.youtube.com/watch?v=kzTDEaQx0us&t=150s) sys.settrace() 存在全局性与颗粒度割裂的架构缺陷:一旦注册便必须捕获全局所有函数调用,且在函数内部只能全量开启或全量关闭行级事件。 - [06:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=360s) pdb.set_trace() 执行时首先注册全局追踪函数,随后回溯遍历调用栈为所有上层已存在的栈帧修补挂载 f_trace,以便在下一行代码执行前立刻截获控制权。 - [06:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=360s) pdb 挂起程序的本质是在 trace 函数接收到 frame 参数后启动命令行交互死循环(REPL),只有用户输入 continue 或下一步命令后循环才会退出并恢复运行。 - [10:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=600s) 调试器读取调用栈和局部变量依赖 CPython 原生维护的 frame 链表对象,通过 f_back 沿调用栈回溯,并从 f_locals、f_lineno 和 f_code 中提取上下文。 - [10:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=600s) 动态求值依赖 eval() 和 exec(),而在调试中直接修改局部变量曾因 CPython 的 fast locals 内部数组机制面临同步困难,该设计直到 Python 3.13 PEP 667 才被彻底现代化重构。 - [10:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=600s) 单步调试命令(如 step、next、return)本质上不修改任何指令指针,而是通过跟踪函数在每一行事件触发时动态判断是否符合用户指定的停靠条件。 - [15:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=900s) 传统追踪开销极其昂贵:在包含紧密循环或密集调用的无关逻辑场景下,哪怕仅在一个无关函数打断点,反复触发的回调也会导致运行时间膨胀一两百倍。 - [15:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=900s) Python 3.12 的 sys.monitoring (PEP 669) 实现了针对单个 code object 的事件监控,使无关函数如 fib() 可以完全绕开监控并以原生速度运行。 - [15:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=900s) sys.monitoring 允许调试器在运行时动态注销循环体内特定非断点行的事件,从而消除了循环迭代中反复派发 Python 级回调的开销。 - [20:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=1200s) 相比过去被动接收全量事件并在 pdb 内部做过滤,sys.monitoring 要求调试器主动在正确位置编排事件订阅,极大增加了调试器的底层设计复杂度。 - [20:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=1200s) 标准库 pdb 因向下兼容和稳定性考量尚未正式合并 sys.monitoring 后端,但主流生态工具(pydevd、debugpy、coverage.py)已率先采用并证实了百倍加速收益。
## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=0s) 演讲开场与 Debugger 的基本需求
在演讲正式开始前,主持人提醒台下听众最后会预留约 5 分钟的 Q&A 提问环节,并指出通道上的立式麦克风位置。随后,主讲人高天登台致谢并开启话题。在正式切入正题前,高天在现场做了一个简单的举手调查:“有多少人用过 Python debugger?又有多少人知道它是如何工作、为什么能工作的?”看到台下热烈的反应,高天开玩笑说大家既然都懂那就讲完了可以散场了,随即言归正传指出,今天的主题正是揭秘 Python debugger(以 pdb 为代表)底层的运作机制,并探讨如何通过系统级优化将其运行速度提升高达 100 倍。
高天引导大家思考:debugger 本质上也只是一个普通的程序,那么要让一个程序成为真正可用的调试器,到底需要具备哪些核心功能?他总结了四个最核心的能力要求: 1. **任意位置暂停**:必须能够让正在运行的目标程序在任意指定的位置中断挂起,从而让开发者能够介入并检查程序状态。 2. **获取运行时信息**:必须能够读取程序当前的上下文信息,例如当前的局部变量(local variables)、调用栈帧(call stacks)等,帮助开发者理解程序正在做什么、当前处于何种状态。 3. **动态执行代码(辅助但强大)**:虽然不是绝对必须的硬指标,但如果支持动态求值(evaluation)就会极具价值——比如执行任意表达式或语句、临时调用某些函数、甚至直接在运行时为局部变量赋新值。 4. **控制程序执行流**:必须能够细粒度控制程序的后续执行节奏,例如单步跳入(step into)、单步跳过/跳出(step out/over)等,“ask your program to run at a pace that you desire. Like, stop wherever you want so that you can, you know, understand what the program is doing.”
```mermaid flowchart TD accTitle: Debugger核心功能架构 accDescr: 展示一个合格的Debugger所必须具备的四个核心能力特征
subgraph CoreCapabilities["Debugger 核心能力要求"] A["1. 任意位置挂起程序 (Pause Execution)"] B["2. 获取运行时上下文 (Inspect Stack & Locals)"] C["3. 动态代码执行与求值 (Dynamic Evaluation)"] D["4. 步进控制执行流 (Step Into / Over / Out)"] end ```
高天特别强调,本次 Talk 讨论的全部范围限定在“进程内调试器”(in-process debugger),即调试器与被调试程序运行在完全相同的同一个 Python 进程内部。虽然业界也存在跨进程的 Out-of-process debugger,但两者的架构和底层原理大不相同,不在本次讨论之列。
---
[02:30](https://www.youtube.com/watch?v=kzTDEaQx0us&t=150s) 从 breakpoint() 到 sys.settrace() 底层机制
在日常开发中,大家的调试习惯各不相同:有些人喜欢使用 VS Code、PyCharm 等 IDE 提供的图形化 GUI 调试器,而有些人则偏爱命令行调试。使用 Python 标准库调试器 pdb 时,现代 Python(3.7+)中最常见的做法是在源码中直接插入内置函数 `breakpoint()`。而对于有较长 Python 使用经验的老程序员来说,大家更习惯的写法是 `pdb.set_trace()`,这两者在底层做的事情几乎完全一致。
“breakpoint” 是全行业所有编程语言通用的标准术语,但在 Python 调试生态中,却有着 `set_trace` 这样一个奇怪的术语。高天解释道,这正是解开所有 Python 调试器魔法的钥匙——它与 Python 底层的跟踪机制 `sys.settrace` 密不可分。`sys.settrace()` 接受一个全局的跟踪函数(trace function),或者说回调函数(callback function)。这个回调函数会在解释器发生特定事件时被触发,主要包括四种核心事件类型: - **`call` 事件**:某个函数即将被调用进入。 - **`return` 事件**:某个函数即将返回。 - **`line` 事件**:某一行代码即将被执行。 - **`exception` 事件**:程序抛出了异常。
```mermaid flowchart TD accTitle: sys.settrace 事件派发与函数粒度控制 accDescr: 展示sys.settrace注册后全局函数调用派发及返回值对单函数跟踪的影响
A["调用 sys.settrace(global_trace_func)"] --> B["全局函数调用触发 'call' 事件"] B --> C{"global_trace_func 返回什么?"} C -->|"返回 local_trace_func"| D["追踪该函数内部后续事件<br>('line', 'return', 'exception')"] C -->|"返回 None"| E["完全忽略该函数内部事件<br>(函数粒度过滤)"] ```
`sys.settrace` 的运作规则十分独特:当通过该机制注册了一个全局 trace 函数后,接下来解释器中**每一个函数的调用**都会强制触发一个 `call` 事件并执行该回调。但这个全局 trace 函数并不能直接监听该函数内部具体的每一行代码;相反,它的职责是返回“另一个局部 trace 函数”。如果它返回了一个新的 trace 函数,解释器后续才会用这个返回的函数去监听该函数体内部的 `line`、`return` 和 `exception` 事件;如果它返回 `None`,则意味着告诉解释器“我对这个特定函数内部发生的一切都不感兴趣”,解释器便会跳过该函数后续所有行级事件。
高天强调了这一设计的两面性: - **全局性**:“that this specific mechanism is a global mechanism. Which means as long as you set_trace, all of the function calls will trigger the call event.” 开发者无法做到仅监听某几个特定函数的调用而不触发其它函数的 `call` 事件,一旦开启就是全局介入。 - **粗粒度与细粒度的割裂**:事件控制的最小颗粒度是“函数级”。对于某一个具体的函数,你只能选择全开(捕获其内部所有事件)或者全关(返回 `None` 彻底不捕获),不能在函数内部仅选择性开启某几行。
此外,高天补充说明,Python 也支持通过给特定的 frame 对象的 `f_trace` 属性手动赋值,将 trace 函数直接挂载到当前已经执行了一半的局部栈帧上,这为接下来剖析 pdb 的实现埋下了伏笔。
---
[06:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=360s) pdb.set_trace() 的执行流程与 REPL 交互本质
弄清了 `sys.settrace` 的原理后,高天揭示了 `pdb.set_trace()` 的内部实现。许多人以为 `pdb.set_trace()` 有某种黑魔法,但它在本质上就是一个极其普通的 Python 函数,并没有任何深奥莫测的特殊语法支持。
当代码中执行到 `pdb.set_trace()` 时,它只完成了几项关键操作: 1. **注册全局 Trace**:调用 `sys.settrace()` 注册自己的跟踪回调函数,确保之后的所有函数调用事件都会通知到 pdb。 2. **回溯并修补上层栈帧**:因为按照标准机制,局部 trace 函数必须在函数刚刚被 `call` 时返回才能捕获行事件,但此时所有处于调用栈上层的函数(caller 以及更外层的 frames)早已经完成调用了。为了解决这个问题,`pdb.set_trace()` 会手动遍历调用栈上的所有外层帧,逐个将它们的 `f_trace` 设置为该跟踪函数。 3. **静候下一次行事件**:完成设置后,`pdb.set_trace()` 函数直接返回。紧接着,被调试程序准备执行下一行代码,此时立刻触发下一个 `line` 事件,调试器的回调函数随之被激活。
```mermaid flowchart TD accTitle: pdb.set_trace 内部初始化流程 accDescr: 展示执行pdb.set_trace时注册全局追踪与追溯上层栈帧的过程
Start["代码执行 pdb.set_trace()"] --> RegGlobal["调用 sys.settrace(pdb_trace)<br>确保捕获未来所有的 'call' 事件"] RegGlobal --> LoopFrames["回溯遍历调用栈所有上层已存在的帧<br>(Frame Hierarchy)"] LoopFrames --> PatchFrame["手动将每个栈帧的 f_trace 赋予 trace 函数"] PatchFrame --> Return["pdb.set_trace() 执行完毕返回"] Return --> NextLine["程序准备执行下一行代码<br>立即触发 'line' 事件"] NextLine --> EnterREPL["pdb 收到当前 frame 参数<br>启动类似 REPL 的交互循环挂起程序"] ```
当 trace 函数在 `line` 事件中被触发时,解释器会将当前正在执行的栈帧对象(`frame`)作为参数传给它。pdb 在拿到当前 frame 之后,其暂停程序的机制非常直观粗暴:它直接在 trace 函数内部启动了一个类似于交互式终端 REPL 的命令行死循环(`while True` 配合读取标准输入 `input()`)。只要用户在控制台中没有输入 `c`(continue)或者下一步指令,这个死循环就不会退出,整个程序的正常执行流也就被牢牢“卡住”在调试器的这一行中。
一旦用户在控制台输入了 `continue`,交互循环退出,trace 函数返回,解释器才会继续往下运行下一行代码,并在下一个满足条件的事件处再次进入该循环。高天总结道,这就是 pdb 暂停程序以及控制进入交互界面的底层全貌。
至于调试器如何提取运行时的信息(例如调用栈信息和局部变量),高天指出这在 Python 中同样没有任何魔法。因为 CPython 运行时本身就已经通过链表形式把完整的调用栈帧(`frame` 链表,每个 frame 包含 `f_back`、`f_locals` 等属性)组装完毕,调试器只需要直接读取传进来的 `frame` 对象的内置属性,就能轻而易举地拿到当前的全部局部变量和完整的调用链轨迹。
[10:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=600s) Inspecting Execution Frames and Controlling Program Flow
Python debuggers rely directly on the execution frames exposed by the CPython runtime, meaning that most of the foundational machinery for inspection requires no special magic. When a trace function is invoked, it receives the active execution frame as an argument. Through the `f_back` attribute on that frame, a debugger can traverse backward up the call chain, which forms a singly linked list representing the entire call stack. Every single frame in this chain gives direct access to the program's runtime state: local variables are available in `frame.f_locals`, the line currently being executed is tracked in `frame.f_lineno`, and the associated code object resides in `frame.f_code`. From `f_code`, one can retrieve static metadata such as the function name and the filename where the code was defined. As Gao Tian puts it to the audience: “You don't even need a debugger to access these functions; you can just write your own program and, you know, access the frame and all those things.”
Beyond reading variables, a full-featured debugger must allow interactive execution, such as evaluating arbitrary user expressions (`x * 2`) or mutating state inside the suspended frame. This is conceptually implemented by running strings through Python's built-in `eval()` and `exec()` functions. However, writing back mutated local variables into a running frame has historically been tricky: in CPython, function-level locals are stored in an optimized fast array (`fast locals`) rather than a standard dictionary. Modifying `frame.f_locals` required specialized C-level hacks to copy values from the dictionary back into the fast variable array, an area that has undergone major redesign in Python 3.13 via PEP 667.
When it comes to stepping and execution control, debuggers offer commands like `step`, `next`, `return`, and `continue`—vocabulary largely inherited from GDB. Gao Tian explains that behind these varied names, the underlying mechanic is identical: determining where and when to halt execution. Because `sys.settrace()` triggers a line event immediately before any bytecode line runs, the debugger already pauses before every execution step. The sole responsibility of the debugger is deciding whether the upcoming line matches the user's requested stopping condition.
- **Stack traversal**: `frame.f_back` chains execution frames into a linked list from the current execution site all the way to module scope. - **Frame state introspection**: `frame.f_locals` exposes local variables, `frame.f_lineno` provides the active line number, and `frame.f_code` holds file paths and function identifiers. - **Expression evaluation**: Uses Python's native `eval()` and `exec()` execution contexts against the current frame's namespaces. - **Fast locals caveat**: Function locals reside in CPython's fast variable slots rather than normal dictionaries, requiring synchronization tricks until PEP 667 modernized `f_locals`. - **Stepping logic**: Commands like `step`, `next`, and `return` do not manipulate the instruction pointer directly; they evaluate whether the upcoming line event fulfills the stop criteria.
```mermaid flowchart TD accTitle: Trace Hook Frame Inspection and Execution Control accDescr: Diagram showing how a trace hook receives an execution frame, inspects stack frames, and checks break conditions. subgraph StackTraverse["Call Stack (Linked List)"] F1["Current Frame (frame)"] -->|"f_back"| F2["Caller Frame (frame.f_back)"] F2 -->|"f_back"| F3["Root Frame (...)"] end
subgraph FrameData["Frame Metadata & State"] F1 -->|"f_locals"| Locals["Local Variables (Fast Locals)"] F1 -->|"f_lineno"| Lineno["Current Line Number"] F1 -->|"f_code"| CodeObj["Code Object (Name, File)"] end
subgraph ExecControl["Debugger Decision Loop"] Lineno --> Decision{"Is this line / frame a target stop?"} Decision -->|"Yes (step/breakpoint hit)"| Prompt["Prompt PDB REPL (eval/exec)"] Decision -->|"No"| Resume["Resume Execution until next event"] end ```
---
[15:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=900s) The Tracing Overhead Crisis and the Promise of `sys.monitoring`
While `sys.settrace()` makes implementing a debugger straightforward, it introduces severe runtime overhead. Because standard tracing operates at a coarse granularity, setting a trace function causes CPython to emit an event for virtually every single line of code and every function call across the entire process. Gao Tian demonstrates this bottleneck with a simple benchmark: a test script defining a trivial `add()` function and a recursive Fibonacci function calculating `fib(24)`. Without any debugger attached, the code finishes almost instantly. However, attaching PDB and setting a single breakpoint inside `add()` inflates runtime to approximately 416 milliseconds—a 100x to 200x slowdown, even though the breakpoint has nothing to do with the heavily executed Fibonacci loop.
This overhead occurs because `sys.settrace()` must fire thousands of call events and line events inside `fib()` just in case the debugger wants to inspect them. While in theory a debugger could return `None` from a call event to disable line tracing inside that specific function frame, PDB does not optimize this aggressively. Furthermore, recursive functions still generate massive volumes of call events regardless, and any function containing a tight loop alongside a breakpoint will fire line events on every single iteration. Gao Tian underscores the core architectural flaw: “The granularity is just too coarse: you can either set all of the call events or not, and in a function, you can either enable all line events or none.”
To resolve this bottleneck, Python 3.12 introduced PEP 669 with the `sys.monitoring` API. Unlike `sys.settrace()`, `sys.monitoring` is an event monitoring system that can register listeners locally per code object. This allows a debugger to enable call events strictly on the specific functions it cares about, leaving functions like `fib()` completely unmonitored. Crucially, `sys.monitoring` also supports selectively disabling events on arbitrary lines at runtime. If a debugger hits a line event inside a loop and determines that line is not a breakpoint target, it can instruct CPython to deactivate event generation on that specific line, ensuring subsequent loop iterations run at full native speed without interrupting Python execution.
- **Performance penalty**: A single breakpoint in an unrelated function caused a recursive `fib(24)` test to slow down by ~100x (taking ~416 ms). - **Trace granularity limitations**: `sys.settrace()` forces global dispatching of call events and blanket subscription to line events across active frames. - **Per-code-object monitoring**: `sys.monitoring` (PEP 669) permits activating callbacks only on targeted functions rather than globally across the interpreter. - **Arbitrary line disabling**: Debuggers can dynamically deregister events on non-breakpoint lines within loops, eliminating repetitive callback overhead.
```mermaid flowchart LR accTitle: Comparison between sys.settrace and sys.monitoring accDescr: Contrasting global event dispatching in sys.settrace with selective code-level dispatching in sys.monitoring. subgraph LegacyTrace["Legacy: sys.settrace()"] C1["Interpreter"] -->|"Fires every call event"| T1["Global Trace Hook"] C1 -->|"Fires every line event"| T1 T1 -->|"PDB filters events in Python"| PDB1["Massive Overhead (100x Slowdown)"] end
subgraph ModernTrace["Modern: sys.monitoring (PEP 669)"] C2["Interpreter"] -->|"Targeted functions only"| Mon["Per-Code-Object Events"] C2 -->|"Unmonitored code (e.g. fib)"| Fast["Native Execution Speed"] Mon -->|"Disable unneeded lines on the fly"| PDB2["Near-Zero Overhead Debugging"] end ```
---
[20:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=1200s) Implementation Complexities, Backward Compatibility, and Ecosystem Adoption
Combining per-code-object monitoring with selective line disabling unlocks the possibility of near-zero-overhead breakpoints, delivering up to a 100x performance gain for Python debugging. However, realizing this potential presents steep engineering challenges. Under the old `sys.settrace()` model, the engine simply dumped all execution events into PDB, leaving PDB's internal logic to filter what to ignore. With `sys.monitoring`, the debugger must actively orchestrate exactly which events are bound to which code objects and dynamically reconfigure event subscriptions as execution advances. As Gao Tian remarks regarding the balance between speed and architectural difficulty: “It's arguably much more complicated, because before, we got every single line event and we could just decide when to stop inside PDB's logic; now we need to make sure that we set the correct events in the correct place.”
The second major hurdle is backward compatibility within the standard library. Changing PDB's internal backend from `sys.settrace()` to `sys.monitoring` must be invisible to end users, preserving expected command behaviors and third-party integrations across different Python versions. Because of these stability constraints, official PDB adoption in the standard library remains an ongoing effort that has not yet landed in upstream releases.
Nevertheless, third-party developer tools across the Python ecosystem have embraced PEP 669 to accelerate debugging and code coverage: - **PyCharm / pydevd**: JetBrains' debugger backend (`pydevd`) incorporates `sys.monitoring` to eliminate tracing slowdowns. - **VS Code / debugpy**: Microsoft's Python debugging team is actively implementing support for `sys.monitoring` inside `debugpy`. - **Coverage tooling**: Ned Batchelder's `coverage.py` and newer coverage utilities have integrated `sys.monitoring`, documenting substantial runtime speedups in community benchmarks.
Gao Tian closes the presentation by noting that as these tools adopt the new monitoring infrastructure, Python developers will soon have access to debuggers and profiling systems that run orders of magnitude faster than traditional PDB.
## Notable quotes > [15:00](https://www.youtube.com/watch?v=kzTDEaQx0us&t=900s) The granularity is just too coarse: you can either set all of the call events or not, and in a function, you can either enable all line events or none. — 高天
## Mentioned 高天, pdb, Python, CPython, sys.settrace, sys.monitoring, PEP 667, PEP 669, VS Code, PyCharm, GDB, pydevd, debugpy, coverage.py, Ned Batchelder, JetBrains, Microsoft, PyCon US
## Video description 终于把这个东西翻译完了,双语字幕的我今年PyCon US 2024的Talk——What makes a Python debugger possible and how can we make it 100x faster。对pdb背后的机制感兴趣的小伙伴可以看一下!
[Source](https://www.youtube.com/watch?v=kzTDEaQx0us)