← Documents Documentation/bpf/drgn.rst GitHub 원문 ↗

Linux 6.18.37 · BPF

BPF drgn tools

drgn으로 kernel memory의 BPF program·map과 attach 관계를 검사하고 출력 항목을 확장하는 방법을 설명합니다.

Source pathDocumentation/bpf/drgn.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

drgn.rst:1-213

drgn은 kernel UAPI 대신 `/proc/kcore` 또는 vmcore와 vmlinux DWARF를 이용해 kernel data structure를 직접 읽고 pretty-print합니다. `bpf_inspect.py`는 이를 활용해 BPF program과 map의 id·type·name을 나열합니다.

특히 userspace API가 제공하지 않는 `freplace`, `fentry`, `fexit` attach 관계를 추적해 tracing·extension program이 어느 BPF function에 연결됐는지 보여 줍니다.

script는 Python 수준에서 쉽게 customize할 수 있으며, 예제처럼 `struct bpf_prog_aux`를 직접 출력해 verifier·JIT·BTF·trampoline·line info 등의 내부 상태를 조사할 수 있습니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2
3 ==============
4 BPF drgn tools
5 ==============
6
7 drgn scripts is a convenient and easy to use mechanism to retrieve arbitrary
8 kernel data structures. drgn is not relying on kernel UAPI to read the data.
9 Instead it's reading directly from ``/proc/kcore`` or vmcore and pretty prints
10 the data based on DWARF debug information from vmlinux.
11
12 This document describes BPF related drgn tools.
13
14 See `drgn/tools`_ for all tools available at the moment and `drgn/doc`_ for
15 more details on drgn itself.
16
17 bpf_inspect.py
18 --------------
19
20 Description
21 ===========
22
23 `bpf_inspect.py`_ is a tool intended to inspect BPF programs and maps. It can
24 iterate over all programs and maps in the system and print basic information
25 about these objects, including id, type and name.
26
27 The main use-case `bpf_inspect.py`_ covers is to show BPF programs of types
28 ``BPF_PROG_TYPE_EXT`` and ``BPF_PROG_TYPE_TRACING`` attached to other BPF
29 programs via ``freplace``/``fentry``/``fexit`` mechanisms, since there is no
30 user-space API to get this information.
31
32 Getting started
33 ===============
34
35 List BPF programs (full names are obtained from BTF)::
36
37 % sudo bpf_inspect.py prog
38 27: BPF_PROG_TYPE_TRACEPOINT tracepoint__tcp__tcp_send_reset
39 4632: BPF_PROG_TYPE_CGROUP_SOCK_ADDR tw_ipt_bind
40 49464: BPF_PROG_TYPE_RAW_TRACEPOINT raw_tracepoint__sched_process_exit
41
42 List BPF maps::
43
44 % sudo bpf_inspect.py map
45 2577: BPF_MAP_TYPE_HASH tw_ipt_vips
46 4050: BPF_MAP_TYPE_STACK_TRACE stack_traces
47 4069: BPF_MAP_TYPE_PERCPU_ARRAY ned_dctcp_cntr
48
49 Find BPF programs attached to BPF program ``test_pkt_access``::
50
51 % sudo bpf_inspect.py p | grep test_pkt_access
52 650: BPF_PROG_TYPE_SCHED_CLS test_pkt_access
53 654: BPF_PROG_TYPE_TRACING test_main linked:[650->25: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access()]
54 655: BPF_PROG_TYPE_TRACING test_subprog1 linked:[650->29: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog1()]
55 656: BPF_PROG_TYPE_TRACING test_subprog2 linked:[650->31: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog2()]
56 657: BPF_PROG_TYPE_TRACING test_subprog3 linked:[650->21: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog3()]
57 658: BPF_PROG_TYPE_EXT new_get_skb_len linked:[650->16: BPF_TRAMP_REPLACE test_pkt_access->get_skb_len()]
58 659: BPF_PROG_TYPE_EXT new_get_skb_ifindex linked:[650->23: BPF_TRAMP_REPLACE test_pkt_access->get_skb_ifindex()]
59 660: BPF_PROG_TYPE_EXT new_get_constant linked:[650->19: BPF_TRAMP_REPLACE test_pkt_access->get_constant()]
60
61 It can be seen that there is a program ``test_pkt_access``, id 650 and there
62 are multiple other tracing and ext programs attached to functions in
63 ``test_pkt_access``.
64
65 For example the line::
66
67 658: BPF_PROG_TYPE_EXT new_get_skb_len linked:[650->16: BPF_TRAMP_REPLACE test_pkt_access->get_skb_len()]
68
69 , means that BPF program id 658, type ``BPF_PROG_TYPE_EXT``, name
70 ``new_get_skb_len`` replaces (``BPF_TRAMP_REPLACE``) function ``get_skb_len()``
71 that has BTF id 16 in BPF program id 650, name ``test_pkt_access``.
72
73 Getting help:
74
75 .. code-block:: none
76
77 % sudo bpf_inspect.py
78 usage: bpf_inspect.py [-h] {prog,p,map,m} ...
79
80 drgn script to list BPF programs or maps and their properties
81 unavailable via kernel API.
82
83 See https://github.com/osandov/drgn/ for more details on drgn.
84
85 optional arguments:
86 -h, --help show this help message and exit
87
88 subcommands:
89 {prog,p,map,m}
90 prog (p) list BPF programs
91 map (m) list BPF maps
92
93 Customization
94 =============
95
96 The script is intended to be customized by developers to print relevant
97 information about BPF programs, maps and other objects.
98
99 For example, to print ``struct bpf_prog_aux`` for BPF program id 53077:
100
101 .. code-block:: none
102
103 % git diff
104 diff --git a/tools/bpf_inspect.py b/tools/bpf_inspect.py
105 index 650e228..aea2357 100755
106 --- a/tools/bpf_inspect.py
107 +++ b/tools/bpf_inspect.py
108 @@ -112,7 +112,9 @@ def list_bpf_progs(args):
109 if linked:
110 linked = f" linked:[{linked}]"
111
112 - print(f"{id_:>6}: {type_:32} {name:32} {linked}")
113 + if id_ == 53077:
114 + print(f"{id_:>6}: {type_:32} {name:32}")
115 + print(f"{bpf_prog.aux}")
116
117
118 def list_bpf_maps(args):
119
120 It produces the output::
121
122 % sudo bpf_inspect.py p
123 53077: BPF_PROG_TYPE_XDP tw_xdp_policer
124 *(struct bpf_prog_aux *)0xffff8893fad4b400 = {
125 .refcnt = (atomic64_t){
126 .counter = (long)58,
127 },
128 .used_map_cnt = (u32)1,
129 .max_ctx_offset = (u32)8,
130 .max_pkt_offset = (u32)15,
131 .max_tp_access = (u32)0,
132 .stack_depth = (u32)8,
133 .id = (u32)53077,
134 .func_cnt = (u32)0,
135 .func_idx = (u32)0,
136 .attach_btf_id = (u32)0,
137 .linked_prog = (struct bpf_prog *)0x0,
138 .verifier_zext = (bool)0,
139 .offload_requested = (bool)0,
140 .attach_btf_trace = (bool)0,
141 .func_proto_unreliable = (bool)0,
142 .trampoline_prog_type = (enum bpf_tramp_prog_type)BPF_TRAMP_FENTRY,
143 .trampoline = (struct bpf_trampoline *)0x0,
144 .tramp_hlist = (struct hlist_node){
145 .next = (struct hlist_node *)0x0,
146 .pprev = (struct hlist_node **)0x0,
147 },
148 .attach_func_proto = (const struct btf_type *)0x0,
149 .attach_func_name = (const char *)0x0,
150 .func = (struct bpf_prog **)0x0,
151 .jit_data = (void *)0x0,
152 .poke_tab = (struct bpf_jit_poke_descriptor *)0x0,
153 .size_poke_tab = (u32)0,
154 .ksym_tnode = (struct latch_tree_node){
155 .node = (struct rb_node [2]){
156 {
157 .__rb_parent_color = (unsigned long)18446612956263126665,
158 .rb_right = (struct rb_node *)0x0,
159 .rb_left = (struct rb_node *)0xffff88a0be3d0088,
160 },
161 {
162 .__rb_parent_color = (unsigned long)18446612956263126689,
163 .rb_right = (struct rb_node *)0x0,
164 .rb_left = (struct rb_node *)0xffff88a0be3d00a0,
165 },
166 },
167 },
168 .ksym_lnode = (struct list_head){
169 .next = (struct list_head *)0xffff88bf481830b8,
170 .prev = (struct list_head *)0xffff888309f536b8,
171 },
172 .ops = (const struct bpf_prog_ops *)xdp_prog_ops+0x0 = 0xffffffff820fa350,
173 .used_maps = (struct bpf_map **)0xffff889ff795de98,
174 .prog = (struct bpf_prog *)0xffffc9000cf2d000,
175 .user = (struct user_struct *)root_user+0x0 = 0xffffffff82444820,
176 .load_time = (u64)2408348759285319,
177 .cgroup_storage = (struct bpf_map *[2]){},
178 .name = (char [16])"tw_xdp_policer",
179 .security = (void *)0xffff889ff795d548,
180 .offload = (struct bpf_prog_offload *)0x0,
181 .btf = (struct btf *)0xffff8890ce6d0580,
182 .func_info = (struct bpf_func_info *)0xffff889ff795d240,
183 .func_info_aux = (struct bpf_func_info_aux *)0xffff889ff795de20,
184 .linfo = (struct bpf_line_info *)0xffff888a707afc00,
185 .jited_linfo = (void **)0xffff8893fad48600,
186 .func_info_cnt = (u32)1,
187 .nr_linfo = (u32)37,
188 .linfo_idx = (u32)0,
189 .num_exentries = (u32)0,
190 .extable = (struct exception_table_entry *)0xffffffffa032d950,
191 .stats = (struct bpf_prog_stats *)0x603fe3a1f6d0,
192 .work = (struct work_struct){
193 .data = (atomic_long_t){
194 .counter = (long)0,
195 },
196 .entry = (struct list_head){
197 .next = (struct list_head *)0x0,
198 .prev = (struct list_head *)0x0,
199 },
200 .func = (work_func_t)0x0,
201 },
202 .rcu = (struct callback_head){
203 .next = (struct callback_head *)0x0,
204 .func = (void (*)(struct callback_head *))0x0,
205 },
206 }
207
208
209 .. Links
210 .. _drgn/doc: https://drgn.readthedocs.io/en/latest/
211 .. _drgn/tools: https://github.com/osandov/drgn/tree/master/tools
212 .. _bpf_inspect.py:
213 https://github.com/osandov/drgn/blob/master/tools/bpf_inspect.py
214

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

BPF drgn tool 소개

1-16

이 문서는 `(LGPL-2.1 OR BSD-2-Clause)` license를 따르며 BPF 관련 drgn tool을 설명합니다.

drgn script는 임의의 kernel data structure를 가져오는 편리하고 사용하기 쉬운 mechanism입니다. drgn은 data를 읽을 때 kernel UAPI에 의존하지 않습니다.

대신 `/proc/kcore` 또는 vmcore에서 직접 data를 읽고 vmlinux의 DWARF debug information을 기반으로 내용을 pretty-print합니다.

현재 사용할 수 있는 모든 tool은 `drgn/tools`에서, drgn 자체의 자세한 내용은 `drgn/doc`에서 확인할 수 있습니다.

bpf_inspect.py 기능과 주요 용도

17-31

`bpf_inspect.py`는 BPF program과 map을 검사하기 위한 tool입니다. system의 모든 program과 map을 iterate하면서 object의 id, type, name을 포함한 기본 정보를 출력할 수 있습니다.

주요 use case는 `freplace`/`fentry`/`fexit` mechanism으로 다른 BPF program에 attach된 `BPF_PROG_TYPE_EXT`와 `BPF_PROG_TYPE_TRACING` program을 보여 주는 것입니다.

이 연결 정보를 가져오는 userspace API가 없기 때문에 kernel memory를 직접 살펴보는 `bpf_inspect.py`가 이를 보완합니다.

Program과 map 목록 조회

32-48

BTF에서 얻은 full name과 함께 BPF program 목록을 표시하려면 다음 명령을 사용합니다.

% sudo bpf_inspect.py prog
    27: BPF_PROG_TYPE_TRACEPOINT         tracepoint__tcp__tcp_send_reset
  4632: BPF_PROG_TYPE_CGROUP_SOCK_ADDR   tw_ipt_bind
 49464: BPF_PROG_TYPE_RAW_TRACEPOINT     raw_tracepoint__sched_process_exit

BPF map 목록은 다음과 같이 표시합니다.

% sudo bpf_inspect.py map
  2577: BPF_MAP_TYPE_HASH                tw_ipt_vips
  4050: BPF_MAP_TYPE_STACK_TRACE         stack_traces
  4069: BPF_MAP_TYPE_PERCPU_ARRAY        ned_dctcp_cntr

Attach된 tracing·ext program 해석

49-72

`test_pkt_access` BPF program에 attach된 program을 찾으려면 다음처럼 출력 결과를 filter합니다.

% sudo bpf_inspect.py p | grep test_pkt_access
   650: BPF_PROG_TYPE_SCHED_CLS          test_pkt_access
   654: BPF_PROG_TYPE_TRACING            test_main                        linked:[650->25: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access()]
   655: BPF_PROG_TYPE_TRACING            test_subprog1                    linked:[650->29: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog1()]
   656: BPF_PROG_TYPE_TRACING            test_subprog2                    linked:[650->31: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog2()]
   657: BPF_PROG_TYPE_TRACING            test_subprog3                    linked:[650->21: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog3()]
   658: BPF_PROG_TYPE_EXT                new_get_skb_len                  linked:[650->16: BPF_TRAMP_REPLACE test_pkt_access->get_skb_len()]
   659: BPF_PROG_TYPE_EXT                new_get_skb_ifindex              linked:[650->23: BPF_TRAMP_REPLACE test_pkt_access->get_skb_ifindex()]
   660: BPF_PROG_TYPE_EXT                new_get_constant                 linked:[650->19: BPF_TRAMP_REPLACE test_pkt_access->get_constant()]
test_pkt_access와 attach된 BPF program
IDTypeNameLinked relation
650BPF_PROG_TYPE_SCHED_CLStest_pkt_access-
654BPF_PROG_TYPE_TRACINGtest_main650->25: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access()
655BPF_PROG_TYPE_TRACINGtest_subprog1650->29: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog1()
656BPF_PROG_TYPE_TRACINGtest_subprog2650->31: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog2()
657BPF_PROG_TYPE_TRACINGtest_subprog3650->21: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog3()
658BPF_PROG_TYPE_EXTnew_get_skb_len650->16: BPF_TRAMP_REPLACE test_pkt_access->get_skb_len()
659BPF_PROG_TYPE_EXTnew_get_skb_ifindex650->23: BPF_TRAMP_REPLACE test_pkt_access->get_skb_ifindex()
660BPF_PROG_TYPE_EXTnew_get_constant650->19: BPF_TRAMP_REPLACE test_pkt_access->get_constant()

bpf_inspect.py 출력의 program ID, type, name과 trampoline attach 관계를 같은 내용의 열 구조로 정리합니다.

출력에는 id 650인 `test_pkt_access`와 그 function에 attach된 여러 tracing 및 ext program이 나타납니다.

다음 한 줄을 자세히 해석할 수 있습니다.

658: BPF_PROG_TYPE_EXT                new_get_skb_len                  linked:[650->16: BPF_TRAMP_REPLACE test_pkt_access->get_skb_len()]

이 줄은 id 658, type `BPF_PROG_TYPE_EXT`, name `new_get_skb_len`인 BPF program이 id 650, name `test_pkt_access`인 program에서 BTF id 16을 가진 `get_skb_len()` function을 `BPF_TRAMP_REPLACE` 방식으로 교체한다는 뜻입니다.

bpf_inspect.py help와 subcommand

73-92

인자 없이 실행하면 다음 help가 표시됩니다.

% sudo bpf_inspect.py
usage: bpf_inspect.py [-h] {prog,p,map,m} ...

drgn script to list BPF programs or maps and their properties
unavailable via kernel API.

See https://github.com/osandov/drgn/ for more details on drgn.

optional arguments:
  -h, --help      show this help message and exit

subcommands:
  {prog,p,map,m}
    prog (p)      list BPF programs
    map (m)       list BPF maps

`prog` 또는 `p`는 BPF program을, `map` 또는 `m`은 BPF map을 나열합니다. 이 tool은 kernel API로 제공되지 않는 property를 drgn으로 읽습니다.

개발자 맞춤 출력 추가

93-119

이 script는 개발자가 BPF program, map과 다른 object에서 필요한 정보를 출력하도록 쉽게 customize할 수 있게 설계됐습니다.

예를 들어 id 53077인 BPF program의 `struct bpf_prog_aux`를 출력하려면 `tools/bpf_inspect.py`의 `list_bpf_progs()`를 다음처럼 수정합니다.

% git diff
diff --git a/tools/bpf_inspect.py b/tools/bpf_inspect.py
index 650e228..aea2357 100755
--- a/tools/bpf_inspect.py
+++ b/tools/bpf_inspect.py
@@ -112,7 +112,9 @@ def list_bpf_progs(args):
         if linked:
             linked = f" linked:[{linked}]"

-        print(f"{id_:>6}: {type_:32} {name:32} {linked}")
+        if id_ == 53077:
+            print(f"{id_:>6}: {type_:32} {name:32}")
+            print(f"{bpf_prog.aux}")


 def list_bpf_maps(args):

변경은 id가 53077일 때 기존 요약 줄과 함께 `bpf_prog.aux` object를 출력하도록 조건을 추가합니다.

struct bpf_prog_aux 출력 예제

120-208

수정한 script는 다음 결과를 출력합니다.

% sudo bpf_inspect.py p
 53077: BPF_PROG_TYPE_XDP                tw_xdp_policer
*(struct bpf_prog_aux *)0xffff8893fad4b400 = {
        .refcnt = (atomic64_t){
                .counter = (long)58,
        },
        .used_map_cnt = (u32)1,
        .max_ctx_offset = (u32)8,
        .max_pkt_offset = (u32)15,
        .max_tp_access = (u32)0,
        .stack_depth = (u32)8,
        .id = (u32)53077,
        .func_cnt = (u32)0,
        .func_idx = (u32)0,
        .attach_btf_id = (u32)0,
        .linked_prog = (struct bpf_prog *)0x0,
        .verifier_zext = (bool)0,
        .offload_requested = (bool)0,
        .attach_btf_trace = (bool)0,
        .func_proto_unreliable = (bool)0,
        .trampoline_prog_type = (enum bpf_tramp_prog_type)BPF_TRAMP_FENTRY,
        .trampoline = (struct bpf_trampoline *)0x0,
        .tramp_hlist = (struct hlist_node){
                .next = (struct hlist_node *)0x0,
                .pprev = (struct hlist_node **)0x0,
        },
        .attach_func_proto = (const struct btf_type *)0x0,
        .attach_func_name = (const char *)0x0,
        .func = (struct bpf_prog **)0x0,
        .jit_data = (void *)0x0,
        .poke_tab = (struct bpf_jit_poke_descriptor *)0x0,
        .size_poke_tab = (u32)0,
        .ksym_tnode = (struct latch_tree_node){
                .node = (struct rb_node [2]){
                        {
                                .__rb_parent_color = (unsigned long)18446612956263126665,
                                .rb_right = (struct rb_node *)0x0,
                                .rb_left = (struct rb_node *)0xffff88a0be3d0088,
                        },
                        {
                                .__rb_parent_color = (unsigned long)18446612956263126689,
                                .rb_right = (struct rb_node *)0x0,
                                .rb_left = (struct rb_node *)0xffff88a0be3d00a0,
                        },
                },
        },
        .ksym_lnode = (struct list_head){
                .next = (struct list_head *)0xffff88bf481830b8,
                .prev = (struct list_head *)0xffff888309f536b8,
        },
        .ops = (const struct bpf_prog_ops *)xdp_prog_ops+0x0 = 0xffffffff820fa350,
        .used_maps = (struct bpf_map **)0xffff889ff795de98,
        .prog = (struct bpf_prog *)0xffffc9000cf2d000,
        .user = (struct user_struct *)root_user+0x0 = 0xffffffff82444820,
        .load_time = (u64)2408348759285319,
        .cgroup_storage = (struct bpf_map *[2]){},
        .name = (char [16])"tw_xdp_policer",
        .security = (void *)0xffff889ff795d548,
        .offload = (struct bpf_prog_offload *)0x0,
        .btf = (struct btf *)0xffff8890ce6d0580,
        .func_info = (struct bpf_func_info *)0xffff889ff795d240,
        .func_info_aux = (struct bpf_func_info_aux *)0xffff889ff795de20,
        .linfo = (struct bpf_line_info *)0xffff888a707afc00,
        .jited_linfo = (void **)0xffff8893fad48600,
        .func_info_cnt = (u32)1,
        .nr_linfo = (u32)37,
        .linfo_idx = (u32)0,
        .num_exentries = (u32)0,
        .extable = (struct exception_table_entry *)0xffffffffa032d950,
        .stats = (struct bpf_prog_stats *)0x603fe3a1f6d0,
        .work = (struct work_struct){
                .data = (atomic_long_t){
                        .counter = (long)0,
                },
                .entry = (struct list_head){
                        .next = (struct list_head *)0x0,
                        .prev = (struct list_head *)0x0,
                },
                .func = (work_func_t)0x0,
        },
        .rcu = (struct callback_head){
                .next = (struct callback_head *)0x0,
                .func = (void (*)(struct callback_head *))0x0,
        },
}

drgn은 `struct bpf_prog_aux *`의 address와 함께 refcount, map 수, stack depth, id, attach 정보, trampoline, symbol tree, 사용 map, program pointer, load time, BTF, function·line info, exception table, stats, work와 RCU callback field를 DWARF type에 맞춰 구조화해서 보여 줍니다.