这是 drgn 的一个使用例子。

最近在追踪一个 veth 网络设备的问题的时候,发现需要查看 veth 网络设备的 netdev_priv() 获取的私有数据。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# drgn
drgn 0.0.21 (using Python 3.10.6, elfutils 0.187, with libkdumpfile)
For help, type help(drgn).
>>> import drgn
>>> from drgn import NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof
>>> from drgn.helpers.common import *
>>> from drgn.helpers.linux import *
>>> veth_dev = netdev_get_by_name(prog, "veth_xxx0")
>>> veth_priv = Object(prog, prog.type("struct veth_priv *"), address=int(veth_dev)+prog.type("struct net_device").size)
>>> veth_priv
*(struct veth_priv *)0xffff8bb05704a000 = {
    .peer = (struct net_device *)0x6c65645f68746576,
    .dropped = (atomic64_t){
        .counter = (s64)53250772400481,
    },
    ._xdp_prog = (struct bpf_prog *)0xffff8bc342621740,
    .rq = (struct veth_rq *)0x0,
    .requested_headroom = (unsigned int)0,
}

而在学会使用 drgn 之前,使用了如下 bpftrace 脚本来查看 veth 网络设备的私有数据。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/usr/bin/env bpftrace
#include <linux/netdevice.h>

struct veth_rq {
    struct napi_struct  xdp_napi;
    struct napi_struct  *napi; /* points to xdp_napi when the latter is initialized */
    struct net_device   *dev;
    void                *xdp_prog;
};

struct veth_priv {
    struct net_device   *peer;
    void                *dropped;
    void                *xdp_prog;
    struct veth_rq      *rq;
    unsigned int        requested_headroom;
};

kprobe:veth_ndo_xdp_xmit
{
    $dev = (struct net_device *)arg0;
    $flags = arg3;

    printf("sizeof netdev: %d\n", sizeof(struct net_device));
    printf("veth xdp xmit on %d:%s flags:%d\n", $dev->ifindex, $dev->name, $flags);

    $priv = (struct veth_priv *)((uint8 *)$dev + sizeof(struct net_device));
    $rcv = (struct net_device *)$priv->peer;
    if ($rcv != 0) {
        printf("veth rcv netdev is %d:%s\n", $rcv->ifindex, $rcv->name);

        $rcv_priv = (struct veth_priv *)((uint8 *)$rcv + sizeof(struct net_device));
        $rq = (struct veth_rq *)$rcv_priv->rq;
        if ($rq != 0) {
            printf("veth rcv netdev is %d:%s with rq\n", $rcv->ifindex, $rcv->name);

            $napi = (struct napi_struct *)$rq->napi;
            if ($napi != 0) {
                printf("veth rcv netdev is %d:%s with rq with napi\n", $rcv->ifindex, $rcv->name);
            } else {
                printf("veth rcv netdev is %d:%s with rq without napi\n", $rcv->ifindex, $rcv->name);
            }
        } else {
            printf("veth rcv netdev is %d:%s without rq\n", $rcv->ifindex, $rcv->name);
        }
    }

    @xdpxmit[tid] = $dev->ifindex;
}

kretprobe:veth_ndo_xdp_xmit
/@xdpxmit[tid]/
{
    $ifindex = @xdpxmit[tid];
    delete(@xdpxmit[tid]);

    printf("veth xdp xmit on %d return %d\n", $ifindex, retval);
}

END
{
    clear(@xdpxmit);
}
1
2
3
4
5
6
7
8
9
# bpftrace veth_ndo_dp_xmit.bt
Attaching 3 probes...
sizeof netdev: 2432
veth xdp xmit on 84:veth_xxx0 flags:1
veth rcv netdev is 85:veth_yyy0
veth rcv netdev is 85:veth_yyy0 with rq
veth rcv netdev is 85:veth_yyy0 with rq without napi
veth xdp xmit on 84 return -6
^C

对比 drgn,bpftrace 脚本复杂又啰嗦、还无法输出 veth_priv 的所有内容。