Re: [PATCH bpf-next] bpf: Add drgn script to list progs/maps

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Quentin Monnet <quentin@xxxxxxxxxxxxx> [Fri, 2020-02-28 04:51 -0800]:
> 2020-02-26 18:32 UTC-0800 ~ Andrey Ignatov <rdna@xxxxxx>
> > drgn is a debugger that reads kernel memory and uses DWARF to get types
> > and symbols. See [1], [2] and [3] for more details on drgn.
> > 
> > Since drgn operates on kernel memory it has access to kernel internals
> > that user space doesn't. It allows to get extended info about various
> > kernel data structures.
> > 
> > Introduce bpf.py drgn script to list BPF programs and maps and their
> > properties unavailable to user space via kernel API.
> > 
> > The main use-case bpf.py covers is to show BPF programs attached to
> > other BPF programs via freplace/fentry/fexit mechanisms introduced
> > recently. There is no user-space API to get this info and e.g. bpftool
> > can only show all BPF programs but can't show if program A replaces a
> > function in program B.
> > 
> 
> [...]
> 
> > 
> > Signed-off-by: Andrey Ignatov <rdna@xxxxxx>
> > ---
> >   tools/bpf/bpf.py | 149 +++++++++++++++++++++++++++++++++++++++++++++++
> >   1 file changed, 149 insertions(+)
> >   create mode 100755 tools/bpf/bpf.py
> > 
> > diff --git a/tools/bpf/bpf.py b/tools/bpf/bpf.py
> > new file mode 100755
> > index 000000000000..a00d112c0486
> > --- /dev/null
> > +++ b/tools/bpf/bpf.py
> > @@ -0,0 +1,149 @@
> > +#!/usr/bin/env drgn
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +#
> > +# Copyright (c) 2020 Facebook
> > +
> > +DESCRIPTION = """
> > +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.
> > +"""
> > +
> > +import argparse
> > +import sys
> > +
> > +from drgn.helpers import enum_type_to_class
> > +from drgn.helpers.linux import (
> > +    bpf_map_for_each,
> > +    bpf_prog_for_each,
> > +    hlist_for_each_entry,
> > +)
> > +
> > +
> > +BpfMapType = enum_type_to_class(prog.type("enum bpf_map_type"), "BpfMapType")
> > +BpfProgType = enum_type_to_class(prog.type("enum bpf_prog_type"), "BpfProgType")
> > +BpfProgTrampType = enum_type_to_class(
> > +    prog.type("enum bpf_tramp_prog_type"), "BpfProgTrampType"
> > +)
> > +BpfAttachType = enum_type_to_class(
> > +    prog.type("enum bpf_attach_type"), "BpfAttachType"
> > +)
> 
> Hi Andrey, the script looks neat, thanks for this work!
> 
> I tried to run it on my system. Because my kernel is 5.3 and does not have
> "enum bpf_tramp_prog_type", the script crashes on the above assignments. But
> even without that enum, it could be possible to print program and map ids
> and types (even if we don't show the trampolines).
> 
> Do you think it would be worth adding error handling on that block,
> something like:
> 
>     try:
>         BpfMapType = ...
>         BpfProgType = ...
>         BpfProgTrampType = ...
>         BpfAttachType = ...
>     except LookupError as e:
>         print(e) # Possibly add a hint as kernel being too old?
> 
> I understand that printing the BPF extensions is the main interest of the
> script, I'm just thinking it would be nice to use it / tweak it even if not
> on the latest kernel. What do you think?

Hi Quentin,

Thanks for feedback. That's a nice usability improvement indeed.

I'll add something like this and tag you on the github PR (since we're
coming to the conclusion that drgn repo is a better place for it).

> > +
> > +
> > +def get_btf_name(btf, btf_id):
> > +    type_ = btf.types[btf_id]
> > +    if type_.name_off < btf.hdr.str_len:
> > +        return btf.strings[type_.name_off].address_of_().string_().decode()
> > +    return ""
> > +
> > +
> > +def get_prog_btf_name(bpf_prog):
> > +    aux = bpf_prog.aux
> > +    if aux.btf:
> > +        # func_info[0] points to BPF program function itself.
> > +        return get_btf_name(aux.btf, aux.func_info[0].type_id)
> > +    return ""
> > +
> > +
> > +def get_prog_name(bpf_prog):
> > +    return get_prog_btf_name(bpf_prog) or bpf_prog.aux.name.string_().decode()
> > +
> > +
> > +def attach_type_to_tramp(attach_type):
> > +    at = BpfAttachType(attach_type)
> > +
> > +    if at == BpfAttachType.BPF_TRACE_FENTRY:
> > +        return BpfProgTrampType.BPF_TRAMP_FENTRY
> > +
> > +    if at == BpfAttachType.BPF_TRACE_FEXIT:
> > +        return BpfProgTrampType.BPF_TRAMP_FEXIT
> > +
> > +    return BpfProgTrampType.BPF_TRAMP_REPLACE
> > +
> > +
> > +def get_linked_func(bpf_prog):
> > +    kind = attach_type_to_tramp(bpf_prog.expected_attach_type)
> > +
> > +    linked_prog = bpf_prog.aux.linked_prog
> > +    linked_btf_id = bpf_prog.aux.attach_btf_id
> > +
> > +    linked_prog_id = linked_prog.aux.id.value_()
> > +    linked_name = "{}->{}()".format(
> > +        get_prog_name(linked_prog),
> > +        get_btf_name(linked_prog.aux.btf, linked_btf_id),
> > +    )
> > +
> > +    return "{}->{}: {} {}".format(
> > +        linked_prog_id, linked_btf_id.value_(), kind.name, linked_name
> > +    )
> > +
> > +
> > +def get_tramp_progs(bpf_prog):
> > +    tr = bpf_prog.aux.trampoline
> > +    if not tr:
> > +        return
> 
> Same observation here, I solved it with
> 
>     try:
>         tr = bpf_prog.aux.trampoline
>         if not tr:
>             return
>     except AttributeError as e:
>         print(e)
>         return

Yep, sounds good.  Will address in v2 on github as well. Thanks!


-- 
Andrey Ignatov



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux