zl程序教程

您现在的位置是:首页 >  其它

当前栏目

gdb 设置一个函数, 出现4个断点的原因

函数 一个 设置 出现 原因 GDB 断点
2023-09-11 14:16:10 时间

今天在vmlinux上设置断点的时候出现4个地址, 超乎认知了, 仔细得看了一下原因

(gdb) b sched_clock

Breakpoint 1 at 0xffffffff8101cf00: sched_clock. (4 locations)

(gdb) i b

Num Type Disp Enb Address What

1 breakpoint keep y MULTIPLE 

1.1 y 0xffffffff8101cf00 in sched_clock at arch/x86/kernel/tsc.c:75

1.2 y 0xffffffff8101d4fd in tsc_save_sched_clock_state at arch/x86/include/asm/paravirt.h:192

1.3 y 0xffffffff8101d555 in tsc_restore_sched_clock_state at arch/x86/include/asm/paravirt.h:192

1.4 y 0xffffffff810b5c30 in sched_clock at kernel/sched/clock.c:71

仔细得看了一下第一个断点, 正常

unsigned long long sched_clock(void)

 return paravirt_sched_clock();

(gdb) disassemble sched_clock

Dump of assembler code for function sched_clock:

 0xffffffff8101cf00 +0 : push %rbp

 0xffffffff8101cf01 +1 : mov %rsp,%rbp

-------------------------------------------------------------------------------

 0xffffffff8101cf04 +4 : callq *0xffffffff81a463c0

-------------------------------------------------------------------------------

 0xffffffff8101cf0b +11 : pop %rbp

 0xffffffff8101cf0c +12 : retq 

End of assembler dump.

看第二个使用的地方tsc_save_sched_clock_state

void tsc_save_sched_clock_state(void)

 if (!sched_clock_stable())

 return;

 cyc2ns_suspend = sched_clock();

(gdb) disassemble tsc_save_sched_clock_state 

Dump of assembler code for function tsc_save_sched_clock_state:

 0xffffffff8101d4f0 +0 : push %rbp

 0xffffffff8101d4f1 +1 : mov %rsp,%rbp

 0xffffffff8101d4f4 +4 : callq 0xffffffff810b5d30 sched_clock_stable 

 0xffffffff8101d4f9 +9 : test %eax,%eax

 0xffffffff8101d4fb +11 : je 0xffffffff8101d50b tsc_save_sched_clock_state+27 

------------------------------------------------------------------------------

 0xffffffff8101d4fd +13 : callq *0xffffffff81a463c0

------------------------------------------------------------------------------

 0xffffffff8101d504 +20 : mov %rax,0xd1c935(%rip) # 0xffffffff81d39e40 cyc2ns_suspend 

 0xffffffff8101d50b +27 : pop %rbp

 0xffffffff8101d50c +28 : retq 

End of assembler dump.

可以看到函数tsc_save_sched_clock_state在调用sched_clock的时候, 因为tsc_save_sched_clock_state和sched_clock在同一个c文件里面, 就直接把函数sched_clock的肉嵌入进来了, 没有直接调用sched_clock, 为了和c语言的逻辑保持一致, 所以这些地方都需要被打上断点

第三个tsc_restore_sched_clock_state和第二个是一样的

第4个的代码是这样的

unsigned long long __attribute__((weak)) sched_clock(void)

 return (unsigned long long)(jiffies - INITIAL_JIFFIES)

 * (NSEC_PER_SEC / HZ);

}

是个弱符号, 但是从以前的知识来说, 弱符号在编译的时候会被强符号干掉, 在从o文件连接成elf文件的时候就没了, 为什么这个时候又出现了呢

readelf -s vmlinux |grep sched_clock\$

 9679: ffffffff819e8690 16 OBJECT LOCAL DEFAULT 8 __ksymtab_sched_clock

 9680: ffffffff819fc66a 12 OBJECT LOCAL DEFAULT 11 __kstrtab_sched_clock

 9681: ffffffff819f7ac0 8 OBJECT LOCAL DEFAULT 10 __kcrctab_sched_clock

 66460: ffffffff8101ce80 123 FUNC GLOBAL DEFAULT 1 native_sched_clock

 67482: 000000003a26ed11 0 NOTYPE GLOBAL DEFAULT ABS __crc_sched_clock

 72418: ffffffff8101cf00 13 FUNC GLOBAL DEFAULT 1 sched_clock

可以看到其实符号表里面只有一个sched_clock符号, 从地址上可以确定是gdb中的第一个符号, 那么这些信息存在哪里呢

一个有可能的猜想就是在debug section里面

把vmlinux的debug section去掉看看

$strip --strip-debug vmlinux.cp

gdb vmlinux.cp

(gdb) i b

Num Type Disp Enb Address What

1 breakpoint keep y 0xffffffff8101cf04 sched_clock+4 

可以只剩一个了, 所以原因是vmlinux中的debug section的信息符号了符号表的信息, 这样看起来, 在gdb vmlinux的时候, 去掉debug section的时候, 有些函数调用信息会丢失, 光依赖符号表也不准确


gdb kernel debug的进程断点 gdb调试kernel的时候, 如果设置通用函数断点, 比如vfs_read, 就会遇到一堆撞到断点的地方, 比如tty输入一个字符, 就是vfs_read, 没办法调试具体的某一个进程 一种办法就是条件断点, 其实不是很好用, 比如用pid, 但是有时候这个进程还没启动, 比如task的comm来判定, 但是kernel中是不支持strcmp来判断字符串是否相等, 因为需要跑函数
以下是在读《深入理解计算机系统》前面的章节“程序的机器级表示”时,自己动手在linux上使用了gdb对一个简单的C程序进行反汇编,通过不懈的努力终于查清楚弄明白了绝大多数的语句。
        在使用GDB调试程序时,有时遇到看到数据不知所云,必须对照定义才能看懂, 有时还要做些运算才能看出来,这样效率显然不高. 如果需要查看一个变量类的当前值,简单的数据类型还好说,如果遇到一些枚举型的变量或特殊条件,想要看到清晰的数据输出,就会比较繁琐.