Andrey Ignatov | 8c06186 | 2020-03-24 11:51:35 -0700 | [diff] [blame] | 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 |