1

用Bpftrace去透视Linux内核

 1 year ago
source link: https://www.51cto.com/article/740723.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

用Bpftrace去透视Linux内核

作者:云技术趣谈 2022-11-27 11:00:15
如果我们想看看tcp发送数据的大小,我们需要获取第三个(arg2)参数size的值。我们尝试写一个bpftrace的小脚本看一下size的值。

bpftrace是基于bpf实现的追踪工具,之前的文章的分享过bpftool,bpftrace的功能更加强大。

558cfa35894e4c4f52d7795ea4c465ee084dce.jpg

在内核中tcp发送数据是通过tcp_sendmsg 方法实现的

int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);

如果我们想看看tcp发送数据的大小,我们需要获取第三个(arg2)参数size的值。我们尝试写一个bpftrace的小脚本看一下size的值。

执行效果如下:

# bpftrace -e 'k:tcp_sendmsg { @size
Attaching 2 probes...


@size:
[1]                    6 |                                                    |
[2, 4)                 0 |                                                    |
[4, 8)                 0 |                                                    |
[8, 16)                0 |                                                    |
[16, 32)             110 |@@@@@@@@                                            |
[32, 64)             687 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[64, 128)            429 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                    |
[128, 256)            60 |@@@@                                                |
[256, 512)            96 |@@@@@@@                                             |
[512, 1K)             46 |@@@                                                 |
[1K, 2K)              28 |@@                                                  |
[2K, 4K)               8 |                                                    |
[4K, 8K)              10 |                                                    |

可以看到size的直方图,可以看到类似正态分布。

如果你想看看都是哪些进程大量发送数据,可以添加大于 1024 字节过滤器,如下:

# bpftrace -e 'k:tcp_sendmsg /arg2 > 1024/ { printf("PID %d,COMM: %s: %d bytes\n", pid,comm, arg2); }'
Attaching 1 probe...
PID 63700,COMM: kube-apiserver: 1581 bytes
PID 68206,COMM: etcd: 1079 bytes
PID 1784,COMM: kube-scheduler: 2249 bytes
PID 63700,COMM: kube-apiserver: 1581 bytes
PID 63700,COMM: kube-apiserver: 5811 bytes
PID 63700,COMM: kube-apiserver: 1581 byt

前面都是基于 tcp_sendmsg 的size参数去玩的,我们更进一步,看看发送数据包的内容,也就tcp_sendmsg的第一个参数。我们写这样一段脚本

#!/usr/local/bin/bpftrace 
#include <net/sock.h> 
    k:tcp_sendmsg
    {
        @sk[tid] = arg0;
        @size[tid] = arg2;
    }
    
    kr:tcp_sendmsg
    /@sk[tid]/
    {
        $sk = (struct sock *)@sk[tid];
        $size = @size[tid];
        $af = $sk->__sk_common.skc_family;
        if ($af == AF_INET) {
            $daddr = ntop($af, $sk->__sk_common.skc_daddr);
            $saddr = ntop($af, $sk->__sk_common.skc_rcv_saddr);
            $lport = $sk->__sk_common.skc_num;
            $dport = $sk->__sk_common.skc_dport;
            $dport = ($dport >> 8) | (($dport << 8) & 0xff00);
            printf("%-15s %-5d -> %-15s %-5d: %d bytes, retval %d\n",
                $saddr, $lport, $daddr, $dport, $size, retval);
        } else {
            printf("IPv6...\n");
        }
        delete(@sk[tid]);
        delete(@size[tid]);
    }

这段脚本有点多,初学者可能有点懵,我稍微解释一下,脚本有两个挂载点,k代表kprobe,kr代表kretprobe。脚本通过解析 sk 这个结构体,去获取tcp 包源IP和端口、目的IP和端口,这里稍微需要了解一下内核的基础数据结构体。retval 代表tcp_sendmsg 函数返回值,这个bpftrace原生支持的。

执行这个脚本

127.0.0.1       2379  -> 127.0.0.1       1236 : 64 bytes, retval 64
127.0.0.1       1236  -> 127.0.0.1       2379 : 51 bytes, retval 51
127.0.0.1       2379  -> 127.0.0.1       1236 : 38 bytes, retval 38
10.133.18.121   65286 -> 10.133.18.121   6443 : 257 bytes, retval 257

是不是非常有意思,我们基于这些数据生成每个节点之间的网络关系。

责任编辑:武晓燕 来源: 今日头条

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK