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")))关键字。

__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")))关键字。

__attribute__((optimize("O0"))) static void cached_dev_write(struct cached_dev *dc, struct search *s)
{
	struct closure *cl = &s->cl;
	struct bio *bio = &s->bio.bio;
	struct bkey start = KEY(dc->disk.id, bio->bi_iter.bi_sector, 0);
	struct bkey end = KEY(dc->disk.id, bio_end_sector(bio), 0);


__attribute__((optimize("O0"))) static void cached_dev_write(struct cached_dev *dc, struct search *s)
{
    unsigned int cachemode;
    bool shouldwriteback;
    struct closure *cl = &s->cl;
    struct bio *bio = &s->bio.bio;
    struct bkey start = KEY(dc->disk.id, bio->bi_iter.bi_sector, 0);
    struct bkey end = KEY(dc->disk.id, bio_end_sector(bio), 0);

六、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 )宏。

十、gdb过程中,遇到received signal SIGTRAP

Thread 2 received signal SIGTRAP, Trace/breakpoint trap.
0xffffffff81d80e1b in default_idle () at ./arch/x86/include/asm/irqflags.h:51
51        asm volatile("sti; hlt": : :"memory");

解决(针对x86处理器):
(gdb) set $ps&=~(1<<8)

<think>好的,我现在需要解决用户在Ubuntu 20.04中遇到的段错误(核心已转储)问题。根据用户提供的引用信息,他们提到通过参考一篇关于Qt在Ubuntu 20.04下段错误的解决方案,成功使用GDB调试找到了问题。我需要整理出系统性的解决步骤,并确保符合用户的需求。 首先,段错误通常是由于程序访问了未分配的内存地址,比如空指针、数组越界或栈溢出等。用户可能想了解如何生成core dump文件,如何启用它们,以及如何用GDB进行分析。此外,用户可能还需要知道常见的原因和调试方法。 根据引用[^1],用户提到完全按照文章操作,启用了core dump并成功调试。所以,我需要确保步骤中包括启用core dump、设置ulimit、配置sysctl,以及使用GDB分析core文件。另外,可能还需要检查编译时的调试符号是否开启,比如使用-g选项。 接下来,我需要分步骤说明解决方案,包括原因分析、配置系统生成core文件、调试方法,以及常见原因。需要确保每一步都清晰,用户能够跟随操作。同时,生成相关问题,帮助用户扩展理解,比如如何预防段错误,或者其他调试工具的使用。 需要注意的是,用户可能对命令行操作不太熟悉,所以命令示例要准确,如ulimit -c unlimited,sysctl的配置,以及GDB的基本命令。同时,提醒用户安装必要的工具,比如gdb和core文件查看工具。 最后,确保回答结构符合用户要求,使用中文,正确使用LaTeX格式(虽然这个问题中可能用不到太多数学公式,但如果有的话要遵守格式),并添加相关问题。</think>### Ubuntu 20.04段错误(核心已转储)解决方案 #### 一、问题原因分析 段错误(Segmentation Fault)通常由以下原因引起: 1. **非法内存访问**:访问未初始化指针、已释放内存或越界访问数组[^1] 2. **栈溢出**:递归调用过深或局部变量占用空间过大 3. **库依赖问题**:动态链接库版本不兼容或缺失 4. **硬件异常**:内存故障或不兼容驱动 #### 二、配置系统生成core文件 1. 临时启用core dump(重启失效): ```bash ulimit -c unlimited ``` 2. 永久生效配置(修改/etc/security/limits.conf): ```bash sudo nano /etc/security/limits.conf # 添加:* soft core unlimited ``` 3. 设置core文件存储路径: ```bash sudo sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%t ``` #### 三、调试方法 1. **编译时添加调试符号**: ```bash gcc -g -o program program.c ``` 2. 使用GDB分析core文件: ```bash gdb program /tmp/core.xxx (gdb) bt # 查看调用堆栈 (gdb) frame N # 切换到指定堆栈帧 (gdb) print variable # 检查变量值 ``` #### 四、常见问题定位技巧 1. **内存访问验证**: ```bash valgrind --tool=memcheck ./program ``` 2. 动态库检查: ```bash ldd program # 显示依赖库 ``` 3. 信号捕获调试: ```bash catchsegv ./program ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值