GDB 符号检视三件套:`ptype` / `info variables` / `info functions`

调试 NuttX/Vela 这类嵌入式系统时,光会 btprint 远远不够。真正能让你在陌生代码里快速定位、看清结构、批量布点的,是 GDB 的符号检视命令。本文整理三件最常用的:

  • ptype ------ 看类型长什么样
  • info variables ------ 找全局/静态变量在哪
  • info functions ------ 找函数在哪

它们都不打印运行时值,而是从符号表里捞元数据,速度快、不依赖程序运行状态,特别适合 coredump 离线分析。


一、ptype ------ 查看类型定义

ptype(print type)只打印类型的结构,不打印变量的值。

常见用法

命令 作用
ptype <var> 查看变量的类型定义
ptype <type> 直接查看某个类型(struct/union/enum/typedef)
ptype/o <struct> 显示 offset 和 size(调试内存布局神器
ptype/m <class> 只显示成员,不展开继承(C++)
ptype <expr> 查看任意表达式的类型,如 ptype foo->bar[2].baz

它能干什么

  1. 看 struct 字段布局

    复制代码
    (gdb) ptype/o struct task_struct
    /* offset    |  size */  type = struct task_struct {
    /*    0      |     8 */    long state;
    /*    8      |     8 */    void *stack;
    ...

    排查内存对齐、padding、字段偏移问题时,比 offsetof 直观得多。

  2. typedef 穿透ptype size_t 告诉你它实际是 unsigned long,避免猜类型。

  3. enum 取值ptype enum state_e 一次性列出所有枚举值。

  4. 函数签名ptype my_func 显示返回值和参数类型,特别适合调试函数指针。

  5. C++ 类层次ptype 完整展开(含虚函数表、继承),ptype/m 只看自身成员。

  6. 判断指针 vs 数组 :源码里 int a[10]int *a 看着像,ptype 一眼分清。

与近邻命令对比

  • print x / p x → 看
  • ptype x → 看类型
  • whatis x → 类型简版(不展开 struct 成员,一行输出)
  • info types <regex> → 在符号表里搜类型名

排查 crash 看 coredump 时,ptype/o 配合 p *(struct foo *)0xaddr 是定位「字段偏移踩错 / 结构体版本不一致」的常用组合。


二、info variables ------ 查找全局/静态变量

info variables符号表 里捞全局和静态变量,不打印值,只告诉你叫什么、在哪、什么类型

基本用法

命令 作用
info variables 列出所有全局和静态变量(通常很长)
info variables <regex> 用正则过滤,最常用
info variables -n 只看非调试符号(minimal symbols)

典型场景

  1. 找变量定义在哪

    复制代码
    (gdb) info variables ^g_task_list$
    File kernel/sched/task.c:
    42:  struct list_head g_task_list;

    直接告诉你文件:行号 + 类型,比翻代码快。

  2. 模糊搜索 :忘了变量全名,info variables uart.*config 一搜就出。

  3. 看驱动注册了哪些静态结构info variables _ops$ 找所有以 _ops 结尾的(file_operationsuart_ops 之类)。

  4. 核对链接产物 :某个 static 变量是否真的进了最终镜像。

与近邻命令对比

命令 看什么
info variables <re> 全局/静态变量符号
info functions <re> 函数符号
info types <re> 类型名(struct/typedef/enum)
info address <sym> 单个符号的地址
info symbol <addr> 反查:这个地址属于哪个符号
print &var 拿到变量地址(要先知道名字)
ptype var 看类型定义

⚠️ 局部变量不会 出现在 info variables 里 ------ 那是栈上的,要用 info locals / info args 看当前帧。


三、info functions ------ 查找函数符号

info functionsinfo variables 是一对,专门搜函数

基本用法

命令 作用
info functions 列出所有函数(通常爆屏,慎用)
info functions <regex> 正则过滤,最常用
info functions -n 只看非调试符号
info functions -q <re> quiet 模式,不打印文件分组头
info functions -t <type> <re> 按返回类型过滤(较新 GDB)

典型场景

  1. 找函数定义在哪

    复制代码
    (gdb) info functions ^uart_open$
    File drivers/serial/uart.c:
    128:  int uart_open(struct file *filep);

    直接给文件:行号 + 完整签名

  2. 模糊搜索info functions .*_init$ 列出所有 _init 结尾的初始化函数。

  3. 看某模块对外接口info functions ^uart_ 列出 uart 子系统所有函数。

  4. C++ 重载/模板:所有重载版本一并列出,签名各异,方便挑你要打断点的那个。

  5. 静态函数也能找到(只要带调试符号),不像 grep 还要管作用域。

调试组合拳

复制代码
(gdb) info functions probe          # 模糊找入口
(gdb) b *uart_probe                 # 在函数入口下断点
(gdb) info address uart_probe       # 拿函数地址(用于函数指针比对)
(gdb) disas uart_probe              # 反汇编
(gdb) ptype uart_probe              # 看签名
(gdb) rbreak ^uart_                 # 给所有 uart_ 开头函数下断点(批量)

与近邻命令对比

命令 用途
info functions <re> 列出匹配的函数符号
rbreak <re> 给匹配的函数批量下断点(破坏性,会真下断)
disas <func> 反汇编整个函数体
info line <func> 看函数起始行号 → 地址映射
ptype <func> 看函数类型签名(返回值/参数)

四、三件套联动:crash 离线分析的标准流程

假设 coredump 里看到一个陌生符号 uart_priv 出现在栈上:

复制代码
(gdb) info variables ^uart_priv$       # 1. 在哪定义?
File drivers/serial/uart.c:
89:  static struct uart_priv_s uart_priv;

(gdb) ptype uart_priv                  # 2. 类型长啥样?
type = struct uart_priv_s {
    int state;
    struct circbuf rx;
    ...
}

(gdb) ptype/o struct uart_priv_s       # 3. 字段偏移确认
/* offset | size */ ...

(gdb) p uart_priv                      # 4. 当前值是什么
$1 = {state = 2, rx = {...}, ...}

(gdb) info address uart_priv           # 5. 物理地址(对比 dump)
Symbol "uart_priv" is static at address 0x20001234.

再配合 info functions ^uart_ 把整套接口列出来,整个驱动的轮廓 5 分钟内就能摸清。


五、要点回顾

你想知道... 用什么
这个类型长什么样?字段偏移多少? ptype / ptype/o
这个全局变量定义在哪个文件? info variables <regex>
这个函数定义在哪?签名是什么? info functions <regex>
这个地址是哪个符号? info symbol <addr>
这个符号的地址是多少? info address <sym>

三件套的共性:只读符号表,不依赖运行时。哪怕程序根本没跑起来,只要 ELF 带调试信息,就能用。这是它们在 coredump 分析里特别趁手的原因。


References

相关推荐
ttkwzyttk6 天前
GDB函数调用栈管理
gdb
ttkwzyttk6 天前
GDB调试变量、内存与寄存器查看与修改
gdb
ttkwzyttk7 天前
GDB调试简介与调试配置
gdb
modelmd20 天前
GDB 摘要
gdb
源分享21 天前
GDB下载和安装保姆级教程
gdb
modelmd1 个月前
翻译 GDB 官方文档
gdb
kidwjb1 个月前
一次多进程信号量同步失效的排查实录
gdb·进程通信·信号量
炘爚1 个月前
C++11实现线程池:项目实现过程的报错与gdb调试
stl·gdb·shared_ptr
CC城子2 个月前
嵌入式Linux宕机问题GDB调试(一)
gdb·嵌入式软件