0001 .. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
0002
0003 ==============
0004 BPF drgn tools
0005 ==============
0006
0007 drgn scripts is a convenient and easy to use mechanism to retrieve arbitrary
0008 kernel data structures. drgn is not relying on kernel UAPI to read the data.
0009 Instead it's reading directly from ``/proc/kcore`` or vmcore and pretty prints
0010 the data based on DWARF debug information from vmlinux.
0011
0012 This document describes BPF related drgn tools.
0013
0014 See `drgn/tools`_ for all tools available at the moment and `drgn/doc`_ for
0015 more details on drgn itself.
0016
0017 bpf_inspect.py
0018 --------------
0019
0020 Description
0021 ===========
0022
0023 `bpf_inspect.py`_ is a tool intended to inspect BPF programs and maps. It can
0024 iterate over all programs and maps in the system and print basic information
0025 about these objects, including id, type and name.
0026
0027 The main use-case `bpf_inspect.py`_ covers is to show BPF programs of types
0028 ``BPF_PROG_TYPE_EXT`` and ``BPF_PROG_TYPE_TRACING`` attached to other BPF
0029 programs via ``freplace``/``fentry``/``fexit`` mechanisms, since there is no
0030 user-space API to get this information.
0031
0032 Getting started
0033 ===============
0034
0035 List BPF programs (full names are obtained from BTF)::
0036
0037 % sudo bpf_inspect.py prog
0038 27: BPF_PROG_TYPE_TRACEPOINT tracepoint__tcp__tcp_send_reset
0039 4632: BPF_PROG_TYPE_CGROUP_SOCK_ADDR tw_ipt_bind
0040 49464: BPF_PROG_TYPE_RAW_TRACEPOINT raw_tracepoint__sched_process_exit
0041
0042 List BPF maps::
0043
0044 % sudo bpf_inspect.py map
0045 2577: BPF_MAP_TYPE_HASH tw_ipt_vips
0046 4050: BPF_MAP_TYPE_STACK_TRACE stack_traces
0047 4069: BPF_MAP_TYPE_PERCPU_ARRAY ned_dctcp_cntr
0048
0049 Find BPF programs attached to BPF program ``test_pkt_access``::
0050
0051 % sudo bpf_inspect.py p | grep test_pkt_access
0052 650: BPF_PROG_TYPE_SCHED_CLS test_pkt_access
0053 654: BPF_PROG_TYPE_TRACING test_main linked:[650->25: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access()]
0054 655: BPF_PROG_TYPE_TRACING test_subprog1 linked:[650->29: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog1()]
0055 656: BPF_PROG_TYPE_TRACING test_subprog2 linked:[650->31: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog2()]
0056 657: BPF_PROG_TYPE_TRACING test_subprog3 linked:[650->21: BPF_TRAMP_FEXIT test_pkt_access->test_pkt_access_subprog3()]
0057 658: BPF_PROG_TYPE_EXT new_get_skb_len linked:[650->16: BPF_TRAMP_REPLACE test_pkt_access->get_skb_len()]
0058 659: BPF_PROG_TYPE_EXT new_get_skb_ifindex linked:[650->23: BPF_TRAMP_REPLACE test_pkt_access->get_skb_ifindex()]
0059 660: BPF_PROG_TYPE_EXT new_get_constant linked:[650->19: BPF_TRAMP_REPLACE test_pkt_access->get_constant()]
0060
0061 It can be seen that there is a program ``test_pkt_access``, id 650 and there
0062 are multiple other tracing and ext programs attached to functions in
0063 ``test_pkt_access``.
0064
0065 For example the line::
0066
0067 658: BPF_PROG_TYPE_EXT new_get_skb_len linked:[650->16: BPF_TRAMP_REPLACE test_pkt_access->get_skb_len()]
0068
0069 , means that BPF program id 658, type ``BPF_PROG_TYPE_EXT``, name
0070 ``new_get_skb_len`` replaces (``BPF_TRAMP_REPLACE``) function ``get_skb_len()``
0071 that has BTF id 16 in BPF program id 650, name ``test_pkt_access``.
0072
0073 Getting help:
0074
0075 .. code-block:: none
0076
0077 % sudo bpf_inspect.py
0078 usage: bpf_inspect.py [-h] {prog,p,map,m} ...
0079
0080 drgn script to list BPF programs or maps and their properties
0081 unavailable via kernel API.
0082
0083 See https://github.com/osandov/drgn/ for more details on drgn.
0084
0085 optional arguments:
0086 -h, --help show this help message and exit
0087
0088 subcommands:
0089 {prog,p,map,m}
0090 prog (p) list BPF programs
0091 map (m) list BPF maps
0092
0093 Customization
0094 =============
0095
0096 The script is intended to be customized by developers to print relevant
0097 information about BPF programs, maps and other objects.
0098
0099 For example, to print ``struct bpf_prog_aux`` for BPF program id 53077:
0100
0101 .. code-block:: none
0102
0103 % git diff
0104 diff --git a/tools/bpf_inspect.py b/tools/bpf_inspect.py
0105 index 650e228..aea2357 100755
0106 --- a/tools/bpf_inspect.py
0107 +++ b/tools/bpf_inspect.py
0108 @@ -112,7 +112,9 @@ def list_bpf_progs(args):
0109 if linked:
0110 linked = f" linked:[{linked}]"
0111
0112 - print(f"{id_:>6}: {type_:32} {name:32} {linked}")
0113 + if id_ == 53077:
0114 + print(f"{id_:>6}: {type_:32} {name:32}")
0115 + print(f"{bpf_prog.aux}")
0116
0117
0118 def list_bpf_maps(args):
0119
0120 It produces the output::
0121
0122 % sudo bpf_inspect.py p
0123 53077: BPF_PROG_TYPE_XDP tw_xdp_policer
0124 *(struct bpf_prog_aux *)0xffff8893fad4b400 = {
0125 .refcnt = (atomic64_t){
0126 .counter = (long)58,
0127 },
0128 .used_map_cnt = (u32)1,
0129 .max_ctx_offset = (u32)8,
0130 .max_pkt_offset = (u32)15,
0131 .max_tp_access = (u32)0,
0132 .stack_depth = (u32)8,
0133 .id = (u32)53077,
0134 .func_cnt = (u32)0,
0135 .func_idx = (u32)0,
0136 .attach_btf_id = (u32)0,
0137 .linked_prog = (struct bpf_prog *)0x0,
0138 .verifier_zext = (bool)0,
0139 .offload_requested = (bool)0,
0140 .attach_btf_trace = (bool)0,
0141 .func_proto_unreliable = (bool)0,
0142 .trampoline_prog_type = (enum bpf_tramp_prog_type)BPF_TRAMP_FENTRY,
0143 .trampoline = (struct bpf_trampoline *)0x0,
0144 .tramp_hlist = (struct hlist_node){
0145 .next = (struct hlist_node *)0x0,
0146 .pprev = (struct hlist_node **)0x0,
0147 },
0148 .attach_func_proto = (const struct btf_type *)0x0,
0149 .attach_func_name = (const char *)0x0,
0150 .func = (struct bpf_prog **)0x0,
0151 .jit_data = (void *)0x0,
0152 .poke_tab = (struct bpf_jit_poke_descriptor *)0x0,
0153 .size_poke_tab = (u32)0,
0154 .ksym_tnode = (struct latch_tree_node){
0155 .node = (struct rb_node [2]){
0156 {
0157 .__rb_parent_color = (unsigned long)18446612956263126665,
0158 .rb_right = (struct rb_node *)0x0,
0159 .rb_left = (struct rb_node *)0xffff88a0be3d0088,
0160 },
0161 {
0162 .__rb_parent_color = (unsigned long)18446612956263126689,
0163 .rb_right = (struct rb_node *)0x0,
0164 .rb_left = (struct rb_node *)0xffff88a0be3d00a0,
0165 },
0166 },
0167 },
0168 .ksym_lnode = (struct list_head){
0169 .next = (struct list_head *)0xffff88bf481830b8,
0170 .prev = (struct list_head *)0xffff888309f536b8,
0171 },
0172 .ops = (const struct bpf_prog_ops *)xdp_prog_ops+0x0 = 0xffffffff820fa350,
0173 .used_maps = (struct bpf_map **)0xffff889ff795de98,
0174 .prog = (struct bpf_prog *)0xffffc9000cf2d000,
0175 .user = (struct user_struct *)root_user+0x0 = 0xffffffff82444820,
0176 .load_time = (u64)2408348759285319,
0177 .cgroup_storage = (struct bpf_map *[2]){},
0178 .name = (char [16])"tw_xdp_policer",
0179 .security = (void *)0xffff889ff795d548,
0180 .offload = (struct bpf_prog_offload *)0x0,
0181 .btf = (struct btf *)0xffff8890ce6d0580,
0182 .func_info = (struct bpf_func_info *)0xffff889ff795d240,
0183 .func_info_aux = (struct bpf_func_info_aux *)0xffff889ff795de20,
0184 .linfo = (struct bpf_line_info *)0xffff888a707afc00,
0185 .jited_linfo = (void **)0xffff8893fad48600,
0186 .func_info_cnt = (u32)1,
0187 .nr_linfo = (u32)37,
0188 .linfo_idx = (u32)0,
0189 .num_exentries = (u32)0,
0190 .extable = (struct exception_table_entry *)0xffffffffa032d950,
0191 .stats = (struct bpf_prog_stats *)0x603fe3a1f6d0,
0192 .work = (struct work_struct){
0193 .data = (atomic_long_t){
0194 .counter = (long)0,
0195 },
0196 .entry = (struct list_head){
0197 .next = (struct list_head *)0x0,
0198 .prev = (struct list_head *)0x0,
0199 },
0200 .func = (work_func_t)0x0,
0201 },
0202 .rcu = (struct callback_head){
0203 .next = (struct callback_head *)0x0,
0204 .func = (void (*)(struct callback_head *))0x0,
0205 },
0206 }
0207
0208
0209 .. Links
0210 .. _drgn/doc: https://drgn.readthedocs.io/en/latest/
0211 .. _drgn/tools: https://github.com/osandov/drgn/tree/master/tools
0212 .. _bpf_inspect.py:
0213 https://github.com/osandov/drgn/blob/master/tools/bpf_inspect.py