4.3 条件断点、调用栈与求值
前置知识:断点、变量与单步执行
预计时间:15 分钟
练习环境:Python 调试器;文末 GDB 实验需要 C 编译器与 GDB
完成标志:能过滤暂停、切换栈帧,并控制求值副作用
普通断点已经找到零重量这条路径。数据量变大后,你还需要减少无关暂停,并从当前函数沿调用栈追到输入来源。
条件断点不是免费观察
要只检查编号 99999 的记录,可把断点条件设为:
item["id"] == 99999调试器每次经过该位置都要求值。高频循环可能显著变慢,条件也可能抛异常或改变状态。条件应尽量是便宜、只读的表达式,不调用写文件、发请求或修改对象的函数。命中次数和日志断点同样有开销。
调用栈与栈帧
def get_discount_ratio(item_id):
return 0 if item_id == 42 else 0.1
def get_alloy_discount(item_id, weight):
ratio = get_discount_ratio(item_id)
return weight * ratio
def calculate_cost(item):
return item["weight"] - get_alloy_discount(item["id"], item["weight"])
print(calculate_cost({"id": 42, "weight": 100}))把这段程序保存为 discount_trace.py,普通运行应输出 100;在 get_discount_ratio 的 return 行设断点,再调试启动。
在最内层暂停时,调用栈大致包含当前的 get_discount_ratio,以及它的调用者 get_alloy_discount、calculate_cost 和模块入口。不同界面可能把当前帧放在顶部或底部。选择调用者帧只会切换局部变量视图;程序仍停在同一现场,这不是执行历史回放。异步、多线程和优化代码的栈可能不完整或形态不同。
ratio == 0 只是观测值。必须结合需求判断编号 42 是正确特例、数据错误还是未实现逻辑。
Watch 与调试控制台
Watch 适合纯表达式,例如 item["weight"] >= 0。函数调用可能再次命中断点、执行 I/O 或修改状态;递归调用也不保证报错,反而可能启动更多计算。调试控制台改变量可快速验证假设,但结论必须在未篡改的新进程中复现。
GDB 速览
先确认两个工具存在,再在临时目录建立一个最小程序:
command -v cc
command -v gdb
gdb_workdir=$(mktemp -d)
cd "$gdb_workdir"
cat > program.c <<'C'
#include <stdio.h>
int main(void) {
int value = 42;
printf("value=%d\n", value);
return 0;
}
C
cc -Og -g program.c -o program
gdb ./program进入 GDB 后:
break main
run
next
print value
backtrace
continue
quit退出 GDB 后清理:
cd
ls -ld -- "$gdb_workdir"
rm -r -- "$gdb_workdir"GDB 和 LLDB 都常用于原生程序。-g 写入调试信息,-Og 保留适合调试的优化级别,但源码步骤仍不等于逐条机器指令。服务器环境还可使用 core dump、远程调试、日志与剖析工具,并非只有 GDB。命令细节可查 GDB 的 Continuing and Stepping 与 Backtraces。
→ 下一步:综合练习