[optimize]使用systemtap调试用户态程序

用户态调试

classic_tong:[optimize]使用systemtap调试用户态程序

 

摘要

systemtap既能调试内核,也能调试用户态. 然后有if有判断有循环.可编程就代表什么都行,那么给我一个用gdb的理由? 另外,世界之大,请不要忘记DTrace,linux 4.9之后支持.

新手扫盲文档:https://sourceware.org/systemtap/SystemTap_Beginners_Guide/index.html

由于我非常懒,所以只写了些例子在下面.建议先读一下上面的文档知道一下基本概念,之后再读我下面的例子,便基本可以进行简单的实践了.

 

用户态调试的依赖条件

3.5之前的linux需要打开内核选项CONFIG_UTRACE

 grep CONFIG_UTRACE /boot/config-`uname -r`

3.5之后的linux需要打开内核选项CONFIG_UPROBES, CONFIG_UTRACE从这个版本开始被废弃.

 grep CONFIG_UPROBES /boot/config-`uname -r`

 

其他

需要特别强调的是,systemtap除了跟踪函数以外,还可以跟踪行噢,使用:

process("a.out").statement("*@main.c:100")

 

 

例子1:最简单的调试用户态程序

 stap -d /usr/sbin/ls --ldd -e 'probe process("ls").function("xmalloc") {print_usyms(ubacktrace())}' -c "ls /"

 

例子2:查看可以用来调试的函数

stap -L 'process("./nginx").function("*")'  // 查看nginx里的所有函数.并带着参数
stap -l 'process("./nginx").function("*")' // 同上,但是不带参数.

 

例子3:第一个简单的跟踪例子

[root@T9 sbin]# stap -e 'probe process("./nginx").function("ngx_process_events_and_timers"){print("test: xxx\n")}'
test: xxx
test: xxx
...

 

下面的每次运行,会触发上面的打印信息.

[root@T9 sbin]# curl 127.0.0.1:5000
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>JDCLOUD_ALB</center>
</body>
</html>

 

 

例子4:打印变量

找到这个变量的引用
[root@T9 sbin]# stap -L 'process("./nginx").function("ngx_http_process_request_header") '
process("/root/OUTPUT_alb/sbin/nginx").function("ngx_http_process_request_header@src/http/ngx_http_request.c:2184") $r:ngx_http_request_t*
[root@T9 sbin]# stap -L 'process("./nginx").function("ngx_http_process_request_header").return '
process("/root/OUTPUT_alb/sbin/nginx").function("ngx_http_process_request_header@src/http/ngx_http_request.c:2184").return $return:ngx_int_t $r:ngx_http_request_t*

 

自动扩展打印,上面的变量 
[root@T9 sbin]# stap -e 'probe process("./nginx").function("ngx_http_process_request_headers").return {print($$vars, "\n");}' 
WARNING: confusing usage, consider @entry($$vars) in .return probe: identifier '$$vars' at <input>:1:85
 source: probe process("./nginx").function("ngx_http_process_request_headers").return {print($$vars, "\n");}
                                                                                             ^
rev=0x55714398b950 p=0x30 len=0x0 n=0x14 rc=0x7ffea5393130 rv=0x557142aab232 h=0x5571439283d0 c=0x557142a3d91a hh=0x557143928488 r=0x3c0 cscf=0x5571439283d0 cmcf=0x7ffea5393160
^C[root@T9 sbin]# stap -e 'probe process("./nginx").function("ngx_http_process_request_headers").return {print($n, "\n");}'  
WARNING: confusing usage, consider @entry($n) in .return probe: identifier '$n' at <input>:1:85
 source: probe process("./nginx").function("ngx_http_process_request_headers").return {print($n, "\n");}
                                                                                             ^
20
20

例子5:使用脚本

[root@T9 sbin]# cat nginx.stp
//probe process("./nginx").function("ngx_http_process_request_headers") {
probe process("./nginx").function("ngx_http_parse_header_line") {
        print($$vars, "\n");
        print(user_string($b->pos), "\n");
        exit();
}
[root@T9 sbin]# stap -v nginx.stp 
Pass 1: parsed user script and 474 library scripts using 245204virt/42624res/3472shr/39252data kb, in 440usr/40sys/491real ms.
Pass 2: analyzed script: 1 probe, 13 functions, 1 embed, 0 globals using 248112virt/46600res/4496shr/42160data kb, in 40usr/10sys/39real ms.
Pass 3: using cached /root/.systemtap/cache/cd/stap_cd35c88067ad0752258f5c08e9a79aa9_4346.c
Pass 4: using cached /root/.systemtap/cache/cd/stap_cd35c88067ad0752258f5c08e9a79aa9_4346.ko
Pass 5: starting run.
r=0x557143928420 b=0x557143915520 allow_underscores=0x0 c=0x0 ch=0x0 p=0x557143969d60 hash=0x55714398b950 i=0x557142a3da19 state=0x0 lowcase=[...]
User-Agent: curl/7.29.0
Host: 127.0.0.1:5000
Accept: */*


Pass 5: run completed in 20usr/60sys/6613real ms.
[root@T9 sbin]# 

 

 

例子6:复杂的变量打印

[root@T9 sbin]# cat nginx.conn.stp  
//probe process("./nginx").function("ngx_http_process_request_headers") {
probe process("./nginx").function("ngx_http_init_connection").return {
        print(@entry($$vars), "\n");
        print("sa_family: ",  @entry($c->local_sockaddr->sa_family), "\n");
        print("conn: ",  @entry($c$$), "\n");
        print("conn.local_sockaddr: ",  @cast(@entry($c->local_sockaddr), "struct sockaddr_in")$, "\n");
        print("local port: ",  @cast(@entry($c->local_sockaddr), "struct sockaddr_in")->sin_port, "\n");
        print("port: ",  @cast(@entry($c->sockaddr), "struct sockaddr_in")->sin_port, "\n");
//      exit();
}
[root@T9 sbin]# stap -v nginx.conn.stp                                   
Pass 1: parsed user script and 474 library scripts using 245204virt/42624res/3472shr/39252data kb, in 440usr/20sys/473real ms.
Pass 2: analyzed script: 2 probes, 56 functions, 3 embeds, 12 globals using 373612virt/172304res/4824shr/167660data kb, in 1190usr/420sys/1612real ms.
Pass 3: using cached /root/.systemtap/cache/d6/stap_d6e87115e317f5b8522d63cd053dc232_19996.c
Pass 4: using cached /root/.systemtap/cache/d6/stap_d6e87115e317f5b8522d63cd053dc232_19996.ko
Pass 5: starting run.
c=0x557143969e70 i=0x0 rev=0xf sin=0x0 port=0x557143969e70 addr=0x5571439154c1 ctx=0x557143915450 hc=0xf sin6=0x557143919ee8 addr6=0x557143915450 cscf=0x557143919f40
sa_family: 2
conn: {.data=0x0, .read=0x55714398b9b0, .write=0x5571439979c0, .fd=3, .recv=0x557142a7d60a, .send=0x557142a7df72, .recv_chain=0x557142a7d8d4, .send_chain=0x557142a88625, 
.listening=0x557143917960, .sent=0, .revt=0, .log=0x557143915460, .pool=0x557143915400, .type=1, .sockaddr=0x557143915450, .socklen=16, .addr_text={.len=9, 
.data="127.0.0.1\377\377\377"}, .proxy_protocol_addr={.len=0, .data=0x0}, .proxy_protocol_port=0, .ssl=0x0, .asynch=0, .async_fd=0, .udp=0x0, .local_sockaddr=0x5571439581f0, 
.local_socklen=16
conn.local_sockaddr: {.sin_family=2, .sin_port=35091, .sin_addr={...}, .sin_zero=[...]}
local port: 35091
port: 6311

 

 

例子7:条件判断的例子

使用如下命令操作,分别访问端口5000,和5001.会有不同的打印日志:

[root@T9 sbin]# curl -q 127.0.0.1:5000 >/dev/null 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   254  100   254    0     0   240k      0 --:--:-- --:--:-- --:--:--  248k
[root@T9 sbin]# curl -q 127.0.0.1:5001 >/dev/null 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   612  100   612    0     0   386k      0 --:--:-- --:--:-- --:--:--  597k

 

打印输出:

[root@T9 sbin]# cat nginx.conn.stp
//probe process("./nginx").function("ngx_http_process_request_headers") {

global server_port = 0

probe process("./nginx").function("ngx_http_init_connection").return {
        print(@entry($$vars), "\n");
        print("sa_family: ",  @entry($c->local_sockaddr->sa_family), "\n");
        print("conn: ",  @entry($c$$), "\n");
        print("conn.local_sockaddr: ",  @cast(@entry($c->local_sockaddr), "struct sockaddr_in")$, "\n");
        print("local port: ",  @cast(@entry($c->local_sockaddr), "struct sockaddr_in")->sin_port, "\n");
        print("port: ",  @cast(@entry($c->sockaddr), "struct sockaddr_in")->sin_port, "\n");

        server_port = @cast(@entry($c->local_sockaddr), "struct sockaddr_in")->sin_port;

        // 34835 35091
        if (server_port == 34835) {
                printf("hahahahaha 5000: %d\n", server_port);
        }
        printf("ennnnnnnnnnnnnnnnnd\n");
//      exit();
}
[root@T9 sbin]# stap -v nginx.conn.stp
Pass 1: parsed user script and 474 library scripts using 245204virt/42628res/3472shr/39252data kb, in 400usr/20sys/424real ms.
Pass 2: analyzed script: 2 probes, 57 functions, 3 embeds, 15 globals using 373624virt/172320res/4828shr/167672data kb, in 1120usr/350sys/1509real ms.
Pass 3: using cached /root/.systemtap/cache/b3/stap_b3bb3ceb4342deea4b0c8d77121e806b_21075.c
Pass 4: using cached /root/.systemtap/cache/b3/stap_b3bb3ceb4342deea4b0c8d77121e806b_21075.ko
Pass 5: starting run.
c=0x557143969d60 i=0x0 rev=0xf sin=0x0 port=0x557143969d60 addr=0x5571439154c1 ctx=0x557143915450 hc=0xf sin6=0x557143919ee8 addr6=0x557143915450 cscf=0x557143919f40
sa_family: 2
conn: {.data=0x0, .read=0x55714398b950, .write=0x557143997960, .fd=3, .recv=0x557142a7d60a, .send=0x557142a7df72, .recv_chain=0x557142a7d8d4, .send_chain=0x557142a88625, 
.listening=0x557143917828, .sent=0, .revt=0, .log=0x557143915460, .pool=0x557143915400, .type=1, .sockaddr=0x557143915450, .socklen=16, .addr_text={.len=9, .data="127.0.0.1\377\377\377"}, 
.proxy_protocol_addr={.len=0, .data=0x0}, .proxy_protocol_port=0, .ssl=0x0, .asynch=0, .async_fd=0, .udp=0x0, .local_sockaddr=0x5571439581a0, .local_socklen=16
conn.local_sockaddr: {.sin_family=2, .sin_port=34835, .sin_addr={...}, .sin_zero=[...]}
local port: 34835
port: 8864
hahahahaha 5000: 34835
ennnnnnnnnnnnnnnnnd
c=0x557143969d60 i=0x0 rev=0xf sin=0x0 port=0x557143969d60 addr=0x5571439154c1 ctx=0x557143915450 hc=0xf sin6=0x557143919ee8 addr6=0x557143915450 cscf=0x557143919f40
sa_family: 2
conn: {.data=0x0, .read=0x55714398b950, .write=0x557143997960, .fd=3, .recv=0x557142a7d60a, .send=0x557142a7df72, .recv_chain=0x557142a7d8d4, .send_chain=0x557142a88625, 
.listening=0x557143917960, .sent=0, .revt=0, .log=0x557143915460, .pool=0x557143915400, .type=1, .sockaddr=0x557143915450, .socklen=16, .addr_text={.len=9, .data="127.0.0.1\377\377\377"}, 
.proxy_protocol_addr={.len=0, .data=0x0}, .proxy_protocol_port=0, .ssl=0x0, .asynch=0, .async_fd=0, .udp=0x0, .local_sockaddr=0x5571439581f0, .local_socklen=16
conn.local_sockaddr: {.sin_family=2, .sin_port=35091, .sin_addr={...}, .sin_zero=[...]}
local port: 35091
port: 9895
ennnnnnnnnnnnnnnnnd

 

 

posted on 2020-12-09 16:54  toong  阅读(888)  评论(0编辑  收藏  举报