cmake默认release模式不生成调试符号,需显式添加-g(msvc用/zi);推荐用target_compile_options而非全局设置;启用set(cmake_export_compile_commands on)生成compile_commands.json供编辑器精准解析。

直接在CMake中启用-g调试符号
不加 -g,GDB 就看不到变量、调用栈、源码行号——这是最常被忽略的前提。CMake 默认 Release 模式不带调试信息,Debug 模式才默认加 -g,但如果你手动设了 CMAKE_BUILD_TYPE=Release 却又想调试,就必须显式补上。
正确做法是:在 CMakeLists.txt 中针对 Release 配置追加 -g,而不是依赖默认行为:
if(CMAKE_BUILD_TYPE STREQUAL "Release") target_compile_options(my_program PRIVATE -g -O3) endif()
注意:set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g") 也能生效,但不如 target_compile_options 精确——它只作用于目标,不污染全局编译标志。
- 别用
add_compile_options(-g):它对所有目标生效,包括第三方库,可能引发链接冲突或符号膨胀 - Windows 下用 MSVC 时换
/Zi,不是-g;混用会静默失效 -
-g和-O3可共存,现代 GCC(≥9.0)已能较好保留调试体验,不必降级到-O0
CMake生成compile_commands.json供VSCode/Clangd使用
VSCode 的 C/C++ 插件或 clangd 需要准确的编译命令才能跳转定义、提示参数、高亮错误。仅靠 c_cpp_properties.json 手动配容易过时,而 compile_commands.json 是 CMake 自动生成的“真实编译指令快照”。
启用它只需一行:
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
放在 cmake_minimum_required 之后、任何 project() 之前即可。生成后,文件落在 build 目录下,VSCode 会自动识别(需重启窗口或重载窗口)。
- 该文件不含宏定义展开结果,但包含完整
-I、-D、-std等,比手工配置可靠得多 - 如果用 Ninja 而非 Makefile,它照样生成,且更轻量
- 某些旧版 CMake(set(CMAKE_CXX_STANDARD 17) 才能正确导出 C++ 标准选项
避免Debug信息被strip掉:检查链接阶段是否保留调试段
即使编译加了 -g,最终可执行文件仍可能被 strip 掉调试段——常见于 CI 打包脚本、install 规则或误配的 CMAKE_STRIP。
验证方法很简单:
file ./my_program readelf -S ./my_program | grep debug
若输出含 .debug_* 段,说明调试信息还在;若只有 stripped 字样,就已被清空。
- 禁用自动 strip:确保没写
set(CMAKE_STRIP ":")或类似覆盖 - install 时保留调试信息:用
INSTALL(FILES ... COMPONENT debug)分离,或显式加OPTIONAL防止误删 - 交叉编译时特别小心:某些工具链默认启用
--strip-unneeded,得在target_link_libraries后追加-Wl,--build-id辅助定位
GDB启动前必须确认的三件事
运行 gdb ./my_program 却卡在 “no debugging symbols found”,往往不是 CMake 配置问题,而是环境链路断了。
- 当前工作目录是否为 build 目录?源码路径必须和
compile_commands.json记录的一致,否则 GDB 找不到main.c - 是否用了
set follow-fork-mode child?多进程程序(如 fork + exec)里,父进程无调试符号很常见,得切到子进程上下文 - 是否启用了
separate-debug-info?某些发行版(如 Fedora)默认把调试信息抽成.debug文件,需安装debuginfo-install包或手动复制
最省事的验证方式:在 build 目录下直接运行 gdb --args ./my_program,然后 run,再 bt —— 如果能看到函数名和行号,说明整条链路通了。











