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

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

systemtap脚本简化内核模块编程

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

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

// simple_mod.c
// make -C /lib/modules/`uname -r`/build SUBDIRS=`pwd` modules
#include 

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]#

我们梳理一下这些步骤:

  1. 编写模块。
  2. 编写Makefile。
  3. 命令编译。
  4. 模块加载。
  5. 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 
%}

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]#

简单的两步即可:

  1. 编写stap脚本,和编写内核模块几乎一致。
  2. 使用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 
#include 

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序列用断点编号来索引。


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

你可能感兴趣的:(system,tap,gdb)