gdb中不能直接print errno是因为errno是线程局部存储(tls)变量,必须先用thread 切换到目标线程再执行print errno,或用thread apply all print errno批量查看;若失败可调用call get_errno()绕过符号解析问题。

gdb里errno是线程局部的,不能直接用print errno
在多线程C++程序中,errno是线程局部存储(TLS)变量,每个线程有自己独立的errno副本。gdb默认只作用于当前选中的线程,而print errno会读取当前线程的值——如果你没切对线程,看到的就不是目标线程的错误码。
常见错误现象:断点触发后执行print errno,返回0或旧值,但实际某子线程刚调用open()失败了。
- 必须先用
info threads确认所有线程ID和状态 - 用
thread <tid></tid>切换到目标线程(注意:这里的<tid></tid>是gdb内部线程号,不是系统PID) - 切换后再
print errno才有效 - 如果目标线程已退出或阻塞在非信号态,可能无法访问其TLS,
print errno会报Cannot access memory
用thread apply all print errno批量查看所有线程的errno
适合快速筛查哪几个线程发生了系统调用错误,尤其在线程数不多(≤20)时效率高。它等价于对每个活跃线程依次执行thread <tid></tid> + print errno。
使用场景:程序卡死或崩溃前,你想知道是否多个线程同时遭遇EAGAIN或ENOMEM。
- 输出中每行开头的
[Thread <n>]</n>对应gdb线程号,注意和info threads输出比对 - 某些线程可能显示
$1 = <error reading variable: cannot access memory at address></error>——说明该线程已终止或TLS不可访问,跳过即可 - 不推荐在线程数极多(如数百)时使用,会显著拖慢调试响应
通过get_errno()符号强制读取目标线程的errno
当print errno失败(比如提示no symbol "errno"),往往是因为调试信息缺失或编译优化干扰了TLS符号解析。这时可绕过符号名,直接调用glibc提供的线程安全接口:
call (int) get_errno()
get_errno()是glibc内部函数(未声明在头文件中,但符号存在),它总是返回**当前选中线程**的errno值,且不依赖调试符号完整性。
- 必须先
thread <tid></tid>切换到目标线程,再call get_errno() - 返回值是
int,可立即用print /d $或print strerror($)辅助解读 - 若
call get_errno()报Cannot find function get_errno,说明你用的是musl libc或极老glibc(call *__errno_location()
为什么__errno_location()比errno更可靠?
__errno_location()是glibc公开的底层接口,返回指向当前线程errno整数的指针。它不经过宏展开或优化干扰,是调试器读取线程局部errno最底层、最稳定的途径。
典型用法:
thread 3<br>print *(int*)__errno_location()
注意:__errno_location是函数名,必须加括号调用;返回的是int*,所以要解引用。
- 该方式在-O2/O3优化下依然有效,而裸
print errno可能被优化器剔除或映射失败 - 部分ARM64或RISC-V交叉工具链中,
__errno_location符号名可能带平台前缀(如__errno_location@GLIBC_2.17),可用info functions errno搜索确认 - 不要尝试
print __errno_location()(不加*)——那会打印函数地址,不是错误码值
errno的访问本质是读内存,不是“查变量”,所以一切取决于你是否正确切换了线程上下文、目标线程是否存活、以及能否拿到有效的TLS基址——这些细节在崩溃现场最容易出问题。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











