C++ 调试实战(08):IDE 图形化调试与 GDB / LLDB 速查表
前面七篇全是命令行。命令行是调试的「内功」:服务器上、容器里、SSH 远程,只有命令行可用;很多高级功能(dprintf、watch -l、thread apply all bt)也只有命令行里才用得顺手。但日常写代码时,在编辑器里点一下行号就下断点、鼠标悬停就看变量值,确实更舒服。这一篇介绍 VS Code 里的图形化调试配置,以及它和命令行的对应关系,最后把整个系列的命令整理成一张速查表。 这是「C++ 调试实战」系列的第 8 篇,也是最后一篇。IDE 的调试界面本质上只是 GDB / LLDB 的一层「外壳」,理解了前面的命令,图形界面上的每个按钮都知道它在做什么。 一、IDE 调试的本质VS Code、CLion、Xcode、Qt Creator 这些 IDE 自己都不会调试程序,它们是在后台启动 GDB 或 LLDB,把你在界面上的操作翻译成调试器命令: 你在界面上做的 背后执行的命令(大致相当于) 点击行号左侧下断点 break orders.cpp:22 右键断点 → 编辑条件 condition 1 o.quantity >...
C++ 调试实战(07):多线程调试——线程切换、死锁定位与数据竞争
单线程程序出了问题,大不了一步一步走。多线程程序就麻烦了:断点在哪个线程上命中?单步时别的线程在干什么?程序卡住不动了,到底是谁在等谁?而且多线程 bug 常常「一调试就消失」——调试器改变了线程执行的节奏。这一篇讲多线程调试的基本功:查看和切换线程、给所有线程打印调用栈、attach 到一个卡死的进程定位死锁,以及用 ThreadSanitizer 抓数据竞争。 这是「C++ 调试实战」系列的第 7 篇。本篇的所有命令都建立在前几篇的基础上:线程切换之后,每个线程里的调用栈、变量,查看方式和单线程完全一样。 编译多线程程序时,GCC 需要链接线程库。较新的 glibc 已经把 pthread 并入了 libc,直接编译就行;老系统上需要加 -pthread。 一、示例一:两个线程累加同一个计数器1234567891011121314151617181920// race.cpp#include <cstdio>#include <thread>int counter = 0; // 没有任何同步void add_many() { ...
C++ 调试实战(06):崩溃现场分析——段错误、core dump 与 AddressSanitizer
Segmentation fault (core dumped)——大概是每个 C++ 程序员见过最多的一行报错。它只告诉你「程序崩了」,不告诉你崩在哪、为什么崩。这一篇讲三种找到答案的办法:在调试器里运行,崩溃时直接停在现场;事后用 core dump 还原现场;以及用 AddressSanitizer 在内存错误发生的那一刻就抓住它,哪怕这个错误并没有让程序崩溃。 这是「C++ 调试实战」系列的第 6 篇。本篇会大量用到第 04 篇的调用栈知识:崩溃分析的第一步永远是 bt。 一、示例:一个忘了判空的指针123456789101112131415161718192021222324252627// crash.cpp#include <cstdio>#include <string>struct Config { std::string name; int retries;};Config* find_config(const std::string& key) { if (key == ...
C++ 调试实战(05):观察点——谁偷偷改了我的变量?
有一类 bug 特别折磨人:某个变量的值莫名其妙变了,但你翻遍代码也找不到哪里改了它。可能是数组越界写到了隔壁,可能是野指针,可能是另一个线程。断点帮不上忙,因为你根本不知道该在哪一行下断点。这时要用的是观察点(watchpoint):不是「程序执行到某一行时停」,而是「某块内存被修改时停」。 这是「C++ 调试实战」系列的第 5 篇。前面讲的断点是盯着「代码」,观察点是盯着「数据」。 一、一个「账对不上」的 bug下面是一个记账程序:Ledger 里有三个账户余额,外加一个审计总额 audit_total,它应该始终等于三个余额之和。 12345678910111213141516171819202122232425262728// watch.cpp#include <cstdio>struct Ledger { int balances[3]; int audit_total; // 应当始终等于 balances 之和};void deposit(Ledger& l, int idx, int amount) ...
C++ 调试实战(04):调用栈与栈帧——我是怎么走到这里的
程序停在某个函数里,你往往还想知道:是谁调用了它?调用链上每一层当时的参数是多少? 一个函数被十个地方调用,出错的是哪一次?回答这些问题要靠调用栈(call stack)。这一篇用一个递归求阶乘的程序,把 backtrace、切换栈帧、查看栈帧信息,以及「finish 作用于选中帧」这些细节讲清楚。 这是「C++ 调试实战」系列的第 4 篇。前面几篇的命令都是在「当前函数」里操作,这一篇开始把视野扩展到整条调用链。 一、示例程序123456789101112131415// fact.cpp#include <cstdio>int factorial(int n) { if (n <= 1) { return 1; } return n * factorial(n - 1);}int main() { int result = factorial(5); std::printf("5! = %d\n", result); return ...
C++ 调试实战(03):查看与修改变量——print、display、STL 容器与调用函数
程序停下来之后,最常做的事就是「看看现在变量是多少」。调试器在这方面比 printf 强太多:随时想看哪个就看哪个,结构体、容器一次全展开,可以按十六进制、二进制显示,可以算表达式,可以每走一步自动打印,甚至可以直接改掉变量的值、调用程序里的函数,看看「如果是这样会怎样」。 这是「C++ 调试实战」系列的第 3 篇。示例程序仍然是第 00 篇的 orders.cpp。前两篇讲了断点和单步,这一篇讲停下来之后怎么看数据。 本篇大部分操作都停在 line_total 的第 16 行 return total;(break orders.cpp:16,然后 run),第一次命中时处理的是 apple(3 件 × 2.5)。 一、print:打印变量和表达式1.1 基本用法1234(gdb) print o$1 = (const Order &) @0xaaaaaaad3020: {name = "apple", quantity = 3, price = 2.5}(gdb) print o.quantity * o.price$2...
C++ 调试实战(02):运行与单步——next、step、finish 与函数返回值
程序停在断点之后,下一步就是「往前走」:一行一行地走(next),钻进函数里走(step),在函数里看够了就一口气跑到函数返回并看看返回值(finish),或者直接跳出一个很长的循环(until)。这几个命令组合起来,就是调试器里最常用的「方向盘」。 这是「C++ 调试实战」系列的第 2 篇。示例程序仍然是第 00 篇的 orders.cpp。上一篇讲了断点,这一篇讲停下来之后怎么走。 关键行号回顾: 行号 代码 12 / 14 / 16 line_total():计算小计 / 打九折 / return total; 21 / 22 / 24 sum_orders():for 循环 / sum += line_total(o); / return sum; 33 main():double total = sum_orders(orders); 一、先把几个命令放在一起看 命令 GDB LLDB 一句话说明 启动 run / start run st...
C++ 调试实战(01):断点——让程序停在你想停的地方
调试的第一步永远是「让程序停下来」。断点(breakpoint)就是你插在代码里的一面小旗:程序跑到这里就暂停,把现场交给你检查。这一篇把断点的各种用法讲全:按函数、按行号、条件断点、临时断点、忽略次数,以及启用、禁用、删除,最后再介绍两种「停下来但不打扰你」的断点——命中时自动执行命令的断点和 dprintf。 这是「C++ 调试实战」系列的第 1 篇。示例程序沿用第 00 篇的 orders.cpp,Linux 版用 g++ -std=c++20 -g -O0 orders.cpp -o orders 编译,macOS 版编译为 orders.mac。 先回顾一下 orders.cpp 的关键行号: 行号 代码 11–17 double line_total(const Order& o):12 行计算小计,14 行打九折,16 行 return total; 19–25 double sum_orders(const std::vector<Order>& orders):22 行 sum += line_total(o)...
C++ 调试实战(00):调试前的准备——编译选项、调试器与第一次会话
写 C++ 的人,大多数时候靠 printf / std::cout 调试:加一行输出、重新编译、跑一遍、再加一行……程序小的时候够用,程序一大、一涉及指针和多线程,这招就开始力不从心。调试器能做到的事情多得多:让程序停在任意一行、一行一行往下走、钻进函数里看、看函数返回了什么、随时打印甚至修改变量、在变量被改的一瞬间停下来、在程序崩溃时保留现场。 这个系列就专门讲这件事:用 GDB 和 LLDB 调试 C++ 程序。每一篇都配一段可以直接编译的小程序,所有命令和输出都在真实环境里跑过。 这是「C++ 调试实战」系列的第 0 篇。本篇不讲具体命令的细节,只做三件事:搞清楚编译时要加什么选项、把调试器装好、跑通第一次调试会话。 系列导航 篇 主题 你会学到 00 调试前的准备(本篇) -g / -O0、GDB 与 LLDB 的安装、第一次调试会话 01 断点 按行 / 按函数下断点、条件断点、临时断点、启用 / 禁用 / 删除 02 运行与单步 run / continue / next...
Needle 2 技术分析:一个 14 MB 的端侧 Tool Call 引擎
本文基于实测数据,覆盖架构原理、Schema 设计规律、iOS 集成,以及一次把 3 类设备扩到 22 类的完整实验记录。 我最近在做一个 iOS 家电控制 Demo,核心需求是:在手机上、离线、实时响应自然语言指令。用一个大模型的 API 当然能做,但那不是这个 Demo 想探索的东西。 选到 needle2 是因为它把「工具调用」这件事做到了极致的轻量:45M 参数、单一 14 MB 二进制(权重也在里面)、单次会话峰值 RAM 28 MB(官方宣称;后文会给出实测数字)、Apache-2.0 许可。 一、背景needle2 在树莓派 5 上跑 500 tok/s,Apple Vision Pro 上跑 400–1500 tok/s。它不是「小一号的通用 LLM」,而是一个专门为 function call 场景设计的模型。 这篇文章是我在做这个 Demo 过程中积累的分析,从架构原理到踩坑细节,全部基于实测。 二、架构:Simple Attention Network(SAN)needle2 的架构来自 2025 年的一篇论文:arXiv:260...










