ubuntu20.04 搭建kernel调试环境第八篇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=<optimized out>, page=0x0 <irq_stack_union>,
    nr_pages=<optimized out>) at ./include/linux/pagemap.h:242
#2  0xffffffff8136b450 in f2fs_read_data_pages (file=<optimized out>, nr_pages=<optimized out>, pages=<optimized out>,
    mapping=<optimized out>) at fs/f2fs/data.c:1369
#3  f2fs_read_data_pages (file=<optimized out>, mapping=<optimized out>,

四、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内存单元内容

示例:

char data_buf[10];
for (i = 0; i < 10; i++)
            data_buf[i] = 'a' + i;

/* 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=<optimized out>, offset=<optimized out>, filp=<optimized out>, ra=<optimized out>,
    mapping=<optimized out>) 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实际的值。

(gdb) p level
$3 = 1186560
(gdb) n
916        dn->inode_page_locked = true;
(gdb) p level
$4 = 80680

(gdb) p &level
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 )宏。

posted @ 2022-09-29 16:13  geshifei  阅读(63)  评论(0编辑  收藏  举报  来源