• gdb调试技巧


    一、字符串条件断点

    命令:b  [函数名]  if  $_streq(函数参数可得到的变量名, "自己定义的字符串")

    $_streq(str1, str2)是gdb的内置函数,用于判断两个字符串是否相等。

    示例:读“fio”文件时设置断点:

    (gdb) b __do_page_cache_readahead if $_streq(filp->f_path.dentry->d_name.name, "fio")
    Breakpoint 1 at 0xffffffff81179e60: file mm/readahead.c, line 156.
    (gdb) c  ---------设置条件断点后,按C键继续执行
    Continuing.
    (gdb)    --------终端执行cat fio命令,触发断点
    Breakpoint 1, __do_page_cache_readahead (mapping=0xffff88800610b9f0,
        filp=0xffff888005e8ed80, offset=0, nr_to_read=320, lookahead_size=160)
        at mm/readahead.c:156
    156        struct inode *inode = mapping->host;

    类似的gdb内置函数还有:

    $_memeq(buf1, buf2, length)内存值是否相同
    $_regex(str, regex)

    str中是否包含regex字符串(str2指定待匹配的字符串模式)

    $_strlen(str)求字符串长度

    二、数值型变量条件断点

    命令:b 变量表达式  if  条件

    示例:在某一行满足某个条件时,设置断点

    1630        /* preallocate blocks in batch for one dnode page */
    1631        if (flag == F2FS_GET_BLOCK_PRE_AIO &&
    1632                (pgofs == end || dn.ofs_in_node == end_offset)) {
    1633    
    1634            dn.ofs_in_node = ofs_in_node;
    1635            err = f2fs_reserve_new_blocks(&dn, prealloc);
    (gdb) p end
    $24 = 256
    (gdb) p end_offset
    $25 = 873
    (gdb) b 1632 if (pgofs==256 || dn.ofs_in_node==873)-----设置条件断点
    Breakpoint 2 at 0xffffffff8143993d: file fs/f2fs/data.c, line 1634.


    (gdb) c---------设置条件断点后,按C键继续执行
    Continuing.

    Breakpoint 2, f2fs_map_blocks (inode=inode@entry=0xffff8880044cf878, map=map@entry=0xffffc90000267d88, create=create@entry=1,
        flag=flag@entry=5) at fs/f2fs/data.c:1634--------触发断点
    1634            dn.ofs_in_node = ofs_in_node;
    (gdb) p pgofs
    $26 = 256

    三、watch条件断点

    命令:watch 变量表达式  if  条件

    watch命令配合bt命令,可用来检查变量值被什么代码改动了。

    示例:检测page何时上锁示例:

    (gdb) watch ((struct page *)0xffffea0000098b00)->flags if (((struct page *)0xffffea0000098b00)->flags & 0x01 == 1)

    (因为gcc优化了代码,无法直接访问page变量,这里直接访问page的内存地址来访问page成员。如果代码没有优化,可以简单地通过watch  page->flags if (page->flags & 0x01 == 1)来设置断点。)
    Hardware watchpoint 3: ((struct page *)0xffffea0000098b00)->flags
    (gdb) c ---------设置条件断点后,按C键继续执行
    Continuing.

    代码运行一段时间后触发断点

    Hardware watchpoint 3: ((struct page *)0xffffea0000098b00)->flags

    Old value = 4611686018427387904----触发前page->flags的bit0=0
    New value = 4611686018427387905----触发后page->flags的bit0=1
    add_to_page_cache_lru (page=0xffffea0000098b00, mapping=0xffff88800610b9f0, offset=1, gfp_mask=21107658) at mm/filemap.c:830
    830        ret = __add_to_page_cache_locked(page, mapping, offset,
    (gdb) bt  ---------通过bt命令看函数调用链
    #0  add_to_page_cache_lru (page=0xffffea0000098b00, mapping=0xffff88800610b9f0, offset=1, gfp_mask=21107658) at mm/filemap.c:830
    #1  0xffffffff8136b0e1 in f2fs_mpage_readpages (mapping=0xffffea0000098b00, pages=, page=0x0 ,
        nr_pages=) at ./include/linux/pagemap.h:242
    #2  0xffffffff8136b450 in f2fs_read_data_pages (file=, nr_pages=, pages=,
        mapping=) at fs/f2fs/data.c:1369
    #3  f2fs_read_data_pages (file=, mapping=,

    四、x命令检查内存值

    命令: x/内存单元数量格式字母内存单元长度  内存地址

    内存单元数量repeat count,有多少个内存单元
    格式字母format letter,x命令的输出格式

    x(hex)

    d(decimal)

    u(unsigned decimal)

    t(binary)

    f(float)

    a(address)

    i(instruction)

    c(char)

    s(string)

    内存单元长度size letter,每个内存单元的长度

    b:1 byte

    h:2 bytes

    w:4 bytes

    g:8 bytes

    常用组合:

    x/3cb-----输出3个char内存单元内容

    x/4uw----输出4个unsigned int内存单元内容

    示例:

    1. char data_buf[10];
    2. for (i = 0; i < 10; i++)
    3. data_buf[i] = 'a' + i;
    4. /* data_buf地址为0x7fffffffde4e */

    五、gdb条件断点不生效

    示例:
    (gdb) b page_cache_sync_readahead if $_streq(filp->f_path.dentry->d_name.name, "fio")
    Breakpoint 3 at 0xffffffff8117a710: page_cache_sync_readahead. (2 locations)
    (gdb) c
    Continuing.
    Error in testing breakpoint condition:
    value has been optimized out

    Breakpoint 3, page_cache_sync_readahead (req_size=, offset=, filp=, ra=,
        mapping=) at mm/readahead.c:527
    527        if (filp && (filp->f_mode & FMODE_RANDOM)) {  --------不满足条件断点触发条件,但是却能断住指定的函数,不符合预期。

    原因:gcc优化了page_cache_sync_readahead ,gdb运行时没法检查函数参数值,导致即使不满足断点条件,gdb也会停在断点处。

    解决:函数声明加上__attribute__((optimize("O0")))关键字。

    void page_cache_sync_readahead(struct address_space *mapping,
                       struct file_ra_state *ra, struct file *filp,
                       pgoff_t offset, unsigned long req_size) __attribute__((optimize("O0")));

    六、set var修改变量值

    通过set var命令修改变量index的值。

    命令格式: set var 变量=值

    七、变量值莫名其妙被修改

    916行语句没有修改变量level的值,但是gdb p命令显示level值变了,这是由于局部变量index存放在寄存器$rax中导致的,p level显示的是rax中的值,这对代码功能没有影响,代码取level值时取的是level实际的值。

    1. (gdb) p level
    2. $3 = 1186560
    3. (gdb) n
    4. 916 dn->inode_page_locked = true;
    5. (gdb) p level
    6. $4 = 80680
    7. (gdb) p &level
    8. Address requested for identifier "level" which is in register $rax

    如果想在gdb中正确显示level值,可以定义volatile关键字:

    volatile int level;

    八、单步调试代码时,执行顺序乱掉

    这是编译器优化导致的。

    解决方法:函数声明加上__attribute__((optimize("O0")))关键字。

    九、_attribute__((optimize("O0")))关键字引起编译错误

    使用__attribute__((optimize("O0")))关键字声明函数类型时,如果编译错误,大概率是因为函数中用到了一些复杂的宏,比如container_of(常见与list_entry、list_first_entry等)、do { xxx } while( xxx )宏。

  • 相关阅读:
    javaEE -4(11000字详解多线程)
    C-数据结构-平衡二叉树
    【Java】构造方法及类的初始化
    【甄选靶场】Vulnhub百个项目渗透——45、项目四十五:bulldog-1(waf绕过)
    Web3.0 DApp(去中心化应用程序)设计架构
    Kotlin 数据类型详解:数字、字符、布尔值与类型转换指南
    nm命令使用详解,让你加快学习速度
    前端面试题46(vue路由如何根据权限动态控制路由的显示?)
    M1Mac开启x86_64命令行archlinux虚拟机的最佳实践(qemu)
    MATLAB | 那些你不得不知道的MATLAB小技巧(二)
  • 原文地址:https://blog.csdn.net/geshifei/article/details/122414969