Linux Systemtap和gdb工具实用技巧两则

mac2024-07-23  53

月末最后一晚,总结简单实用的Linux调试工具使用技巧两则。

systemtap脚本简化内核模块编程

为了测试或者修改Linux内核的一个特性,我们通常会写一个模块,比如打印一些内核的信息,比如修改一个物理页面之类。

以打印hello world以及传入的两个参数为例,我们编写如下最简单的内核模块:

// simple_mod.c // make -C /lib/modules/`uname -r`/build SUBDIRS=`pwd` modules #include <linux/module.h> static unsigned long param1 = 0; module_param(param1, long, 0644); static unsigned long param2 = 0; module_param(param2, long, 0644); static int simple_init(void) { printk("hello world %lx %lx\n", param1, param2); return -1; } static void simple_exit(void) { } module_init(simple_init); module_exit(simple_exit); MODULE_LICENSE("GPL");

为了让这个内核模块起作用,必须编译加载它,首先我们要有个Makefile:

[root@localhost mod]# cat Makefile obj-m = simple_mod.o

然后按照下面的命令编译之:

[root@localhost mod]# make -C /lib/modules/`uname -r`/build SUBDIRS=`pwd` modules

最后,我们加载它,然后用dmesg查看结果:

[root@localhost mod]# insmod ./simple_mod.ko insmod: ERROR: could not insert module ./simple_mod.ko: Operation not permitted [root@localhost mod]# dmesg [ 9807.368562] hello world 0 0 [root@localhost mod]#

我们梳理一下这些步骤:

编写模块。编写Makefile。命令编译。模块加载。dmesg看结果

好麻烦!使用systemtap会简单很多。

systemtap不仅仅可以动态探测debug内核,它可以进入Guru模式嵌入C代码实现一个完整的内核模块,只需要stap加上-g参数:

-g Guru mode. Enable parsing of unsafe expert-level constructs like embedded C.

我们看看这是多么简单:

用 “%{” 开始C代码,用 “%}” 结束C代码。C代码中除了正常的Linux模块代码,还能调用systemtap的函数。

我们用一个小例子展示一下,该例子除了完全实现上述hello world模块的功能之外,还能刷新系统TLB:

// hello_module.stp %{ #include <linux/module.h> %} function flush_tlb_print_test:long(pid:long, addr:long) %{ void (*pflush_tlb_all)(void); // 从/proc/kallsyms查询到的flush_tlb_all的地址。 pflush_tlb_all = (void (*))0xffffffff81066090; // 以STAP_PRINTF替代printk,可以不必再dmesg展示结果,这样结果可以直接打stdout。 // 当然,你也可以继续使用printk,然后用dmesg来看结果 STAP_PRINTF("hello world %llx %llx\n", STAP_ARG_pid, STAP_ARG_addr); pflush_tlb_all(); STAP_RETVALUE = 0; %} probe begin { // $开头为数字参数,@开头为字符串参数 flush_tlb_print_test($1, $2); exit(); }

就这么简单,不再需要Linux内核模块的例行编写方法,这些systemtap都替你做了。

下面我们只需要一步就能看到效果,执行stap命令即可:

[root@localhost stap]# stap -g hello_module.stp 4439 0x10d7010 hello world 1157 10d7010 [root@localhost stap]#

简单的两步即可:

编写stap脚本,和编写内核模块几乎一致。使用stap命令执行stap脚本。

systemtap嵌入C代码比编写独立的内核模块效率要高很多。

gdb断点自动执行命令

昨天,Windows安全超级专家咨询了问题:

windbg有这个功能,bp 0x12345678 “dt xxx; g;”,意思是下个断点,到了断点处print一个东西然后自动继续运行。gdb有类似的功能吗?b *0x12345678 "p xxx; c"这样

其实gdb是有这个功能的,下面简单演示一下。

先看一个测试代码:

// gcc -g test.c -o test #include <stdio.h> #include <stdlib.h> int global = 0; void func() { printf("Process in [%s]\n", __FUNCTION__); } int main() { global ++; func(); return 0; }

编译它:

[root@localhost gdb]# gcc -g test.c -o test

然后gdb调试test,我们希望在func处下个断点,然后打印global变量后无需人工干预继续执行下面的语句:

[root@localhost gdb]# gdb ./test -q Reading symbols from /root/gdb/test...done. (gdb) b func Breakpoint 1 at 0x400521: file test.c, line 8. (gdb) commands 1 Type commands for breakpoint(s) 1, one per line. End with a line saying just "end". >p global >cont >end (gdb) r Starting program: /root/gdb/./test Breakpoint 1, func () at test.c:8 8 printf("Process in [%s]\n", __FUNCTION__); $1 = 1 Process in [func] [Inferior 1 (process 6831) exited normally] (gdb)

为断点写一个commands命令序列即可,其中commands序列用断点编号来索引。


浙江温州皮鞋湿,下雨进水不会胖。

最新回复(0)