月末最后一晚,总结简单实用的Linux调试工具使用技巧两则。
为了测试或者修改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代码比编写独立的内核模块效率要高很多。
昨天,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序列用断点编号来索引。
浙江温州皮鞋湿,下雨进水不会胖。