systemtap 应用笔记

本文介绍了如何安装和使用Systemtap进行调试,包括设置编译开关、查询函数探测点、编写调试脚本等。在用户态程序编译时需开启调试开关并关闭优化,内核编译需配置特定选项。Systemtap可用于打印探测点、调试动态链接库,并提供了丰富的调试脚本编写示例。

安装 systemtap

yum -y install systemtap

编译开关的设置

用户态程序的编译

并不是每个函数都可以探测的。
只有使用了 -g 的调试开关。才可以支持探测。
而且 -O 的优化开关,也会令到探测点减少。
所以最好在编译的时候使用 -O0 -g3,关闭优化,还有使用高一点的调试级别。

内核的编译

要想编译一个支持Systemtap的内核,必须配置这些内核选项:

	Kernel hacking  --->
		[*] Kernel debugging
			[*]   Compile the kernel with debug info
	 
	Instrumentation Support  --->
		[*] Kprobes (EXPERIMENTAL)
	 
	General setup  --->
		[*] Kernel->user space relay support (formerly relayfs)

可以用以下符号grep生成的配置文件.config来确认这些配置是否成功:
如果成功,它们应当都为 Y.

# file: .config

	CONFIG_DEBUG_INFO
	CONFIG_KPROBES
	CONFIG_RELAY

查询函数探测点

使用 stap 的 -l, -L 可以打印出支持的 探测点。

-g:

专家模式。可以打印更多的探测点。

-l:

只打印出 函数探测点 的函数函数名称,路径,和行号。
输出格式如下
function("<function_name>@<source_file_path>:<line_number>")

-L:

-l 的基础上,增加探测的变量列表。
输出格式如下
function("<function_name>@<source_file_path>:<line_number>") <function_variable_list>

使用方法:

# 查询内核的系统调用

	stap -gL 'syscall.*'

# 查询内核的函数探测点

	stap -gL 'kernel.function("<function_name>")' 
	stap -gL 'kernel.function("<function_name>").call'		# 函数入口
	stap -gL 'kernel.function("<function_name>").return'	# 函数出口

# 查询内核模块
	stap -gL 'module("<module_name>").function("<function_name>")'

# 查询可执行文件的函数探测点

	stap -L 'process("<executable_file_path>").function("<function_name>")'
	stap -L 'process("<executable_file_path>").function("<function_name>").call'		# 函数入口
	stap -L 'process("<executable_file_path>").function("<function_name>").return'		# 函数出口

# 查询动态链接库的函数探测点

	stap -L 'process("<shared_library_file_path>").function("<function_name>")'
	stap -L 'process("<shared_library_file_path>").function("<function_name>").call'	# 函数入口
	stap -L 'process("<shared_library_file_path>").function("<function_name>").return'	# 函数出口

# 查询语句探测点

	stap -L 'process("<shared_library_file_path>").statement("<function_name>:@<source_file_path>:*")'

<executable_file_path>:

可执行文件的路径

<shared_library_file_path>:

动态链接库的路径
注意:一定要是 ldconfig -p 所登记的路径。

<function_name>:

函数名称。可以使用通配字符匹配。如果要显示所有函数,可以使用 “*”。

例子:

## 查询系统调用 

	stap -gL 'syscall.*' | grep sendmsg
	>	syscall.compat_sys_sendmsg name:string s:long msg_uaddr:long flags:long flags_str:string argstr:string $fd:int $msg:struct compat_msghdr* $flags:unsigned int
	>	syscall.sendmsg name:string s:long msg_uaddr:long flags:long flags_str:string argstr:string $fd:long int $msg:long int $flags:long int
		
	stap -gL 'syscall.sendmsg.return'
	>	syscall.sendmsg.return name:string retstr:string $return:long int $fd:long int $msg:long int $flags:long int

## 查询内核模块
	stap -gL 'module("xt_REDIRECT").function("redirect_tg4")'

## 查询内核函数探测点
	
	stap -gL 'kernel.function("__neigh_create")'
	>	kernel.function("__neigh_create@net/core/neighbour.c:463") $tbl:struct neigh_table* $pkey:void const* $dev:struct net_device* $want_ref:bool

	stap -gL 'kernel.function("__neigh_create").call'
	>	kernel.function("__neigh_create@net/core/neighbour.c:463").call $tbl:struct neigh_table* $pkey:void const* $dev:struct net_device* $want_ref:bool

	stap -gL 'kernel.function("__neigh_create").return'
	>	kernel.function("__neigh_create@net/core/neighbour.c:463").return $return:struct neighbour* $tbl:struct neigh_table* $pkey:void const* $dev:struct net_device* $want_ref:bool

	
## 查询内核语句探测点

	stap -gL 'kernel.statement("__neigh_create@net/core/neighbour.c:*")'
	>	kernel.statement("__neigh_create@net/core/neighbour.c:464") $tbl:struct neigh_table* $pkey:void const* $dev:struct net_device* $want_ref:bool $key_len:int
	>	...
	>	kernel.statement("__neigh_create@net/core/neighbour.c:489") $tbl:struct neigh_table* $pkey:void const* $dev:struct net_device* $want_ref:bool $key_len:int $error:int
	>	...
	>	kernel.statement("__neigh_create@net/core/neighbour.c:521") $tbl:struct neigh_table* $pkey:void const* $dev:struct net_device* $want_ref:bool $n1:struct neighbour* $nht:struct neigh_hash_table*
	>	...
	>	kernel.statement("__neigh_create@net/core/neighbour.c:548") $tbl:struct neigh_table* $pkey:void const* $dev:struct net_device* $want_ref:bool
	
## 查询用户态函数
	
	which ping
	>	/usr/bin/ping

	stap -gL 'process("/usr/bin/ping").function("*")'
	>	process("/usr/bin/ping").function("__do_global_dtors_aux")
	>	process("/usr/bin/ping").function("__libc_csu_fini")
	>	process("/usr/bin/ping").function("__libc_csu_init")
	>	process("/usr/bin/ping").function("__schedule_exit")
	>	process("/usr/bin/ping").function("_fini")
	>	process("/usr/bin/ping").function("_init")
	>	process("/usr/bin/ping").function("_start")
	>	...
	>	process("/usr/bin/ping").function("main")
	>	process("/usr/bin/ping").function("main_loop")
	>	...
	>	process("/usr/bin/ping").function("pinger")
	>	...

使用调试脚本

用户态调试

stap --ldd -x <pid> <stap_script_file_path>

--ldd:

用于调试动态链接库

<pid>:

进程号

<stap_script_file_path>:

调试脚本路径


编写调试脚本

探测点

探测点描述
beginThe startup of the systemtap session.
endThe end of the systemtap session.
kernel.function(“sys_open”)The entry to the function named sys_open in the kernel.
syscall.close.returnThe return from the close system call.
module(“ext3”).statement(0xdeadbeef)The addressed instruction in the ext3 filesystem driver.
timer.ms(200)A timer that fires every 200 milliseconds.
timer.profileA timer that fires periodically on every CPU.
perf.hw.cache_missesA particular number of CPU cache misses have occurred.
procfs(“status”).readA process trying to read a synthetic file.
process(“a.out”).statement("*@main.c:200")Line 200 of the a.out program.
kernel.function(“xx”).inline {}指定函数被内联时进入
kernel.function(“xx”).call {}指定函数被调用是进入(不含内联)
kernel.function("").return{}可以在该函数返回时执行。

简单的例子:
计数4秒。每一秒计数一次。

/* global counter */
global count;

/* header */
probe begin
{
	count = 0;
	printf("--begin--\n");
	printf("count = %d\n", count);
}

/* footer */
probe end
{
	printf("\n");
	printf("---end---\n");
	printf("count = %d\n", count);
}

/* increasing counter every 1 second */
probe timer.ms(1000) {
    count++;
	printf(".");
}

/* exit after 4 seconds */
probe timer.ms(4000) 
{
    exit()
}

将每隔5秒钟输出系统调用的最多的20个系统调用。

#!/usr/bin/env stap
#
# This script continuously lists the top 20 systemcalls on the system
#
 
global syscalls
 
function print_top () {
        cnt=0
        log ("SYSCALL\t\t\t\tCOUNT")
        foreach ([name] in syscalls-) {
                printf("%-20s\t\t%5d\n",name, syscalls[name])
                if (cnt++ == 20)
                        break
        }
        printf("--------------------------------------\n")
        delete syscalls
}
 
probe kernel.function("sys_*") {
        syscalls[probefunc()]++
}
 
# print top syscalls every 5 seconds
probe timer.ms(5000) {
        print_top ()
}

developerworks systemtap3

基础函数

基础函数描述
tid()The id of the current thread.
pid()The process (task group) id of the current thread.
uid()The id of the current user.
execname()The name of the current process.
cpu()The current cpu number.
gettimeofday_s()Number of seconds since epoch.
gettimeofday_msNumber of milli-seconds since epoch.
gettimeofday_usNumber of micro-seconds since epoch.
get_cycles()Snapshot of hardware cycle counter.
pp()A string describing the probe point being currently handled.
ppfunc()If known, the the function name in which this probe was placed.
print_backtrace()If possible, print a kernel backtrace.
print_ubacktrace()If possible, print a user-space backtrace.
thread_indentindent

用于进程/线程输出时的缩进相当于一个thread_local变量,
参数表示作用在该变量上的一个增量,进入一个函数时参数为正值,退出时为负值。
就可以产生函数调用的缩进效果,下面是一个类似tutorial上的示例:

probe kernel.function("*@fs/open.c").call {
    printf("%s -> %s(%s)\n", thread_indent(4), ppfunc(), $$parms);
}

probe kernel.function("*@fs/open.c").return {
    printf("%s <- %s\n", thread_indent(-4), ppfunc());
}

变量

变量默认的都是局部变量,即每个处理函数内的变量是不共享的。
使用全局变量的话,要在开始使用global关键字进行定义。
变量是弱类型的,可以相互转换但是要手工显式进行。

变量描述
$varname引用变量varname
$var->field"引用结构的成员变量
$var[N]引用数组的元素
&$var变量的地址
@var(“varname”)引用变量varname
@var(“var@src/file.c”)引用src/file.c在被编译时的全局变量varname
@var(“varname@file.c”)->field引用结构的成员变量
@var(“var@file.c”)[N]引用数组的元素
&@var(“var@file.c”)变量的地址
v a r var varprovide a string that includes the values of basic type values,不会打印成员变量
v a r var var$provide a string that includes all values of nested data types,会打印成员变量
$$vars一个包含所有函数参数、局部变量的字符串。
$$locals一个包含所有局部变量的字符串,$$vars子集,仅打印local变量
$$params一个包含所有函数参数的字符串,$$vars子集,仅包含函数参数
$$return仅在return probes存在,如果没有返回值,则是空串
变量描述
$$vars类似于 sprintf(“parm1=%x,… ,parmN=%x var1=%x,… ,varN=%x”, parm1,…, parmN, var1,…, varN)
$$locals类似于 sprintf(“var1=%x,… ,varN=%x”, var1,… ,varN)
$$params类似于 sprintf(“parm1=%x,… ,parmN=%x”, parm1,… ,parmN)
$$return类似于 sprintf(“return=%x”, $return)

ref:
Systemtap
SystemTap Beginner
SystemTap使用技巧【一】
SystemTap Beginner
Linux 调试: systemtap
内核调试神器SystemTap — 探测点与语法(二)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值