bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andrey Ignatov <rdna@fb.com>
To: Quentin Monnet <quentin@isovalent.com>
Cc: <bpf@vger.kernel.org>, <ast@kernel.org>, <daniel@iogearbox.net>,
	<osandov@fb.com>, <kernel-team@fb.com>
Subject: Re: [PATCH bpf-next] bpf: Add drgn script to list progs/maps
Date: Fri, 28 Feb 2020 12:15:14 -0800	[thread overview]
Message-ID: <20200228201514.GB51456@rdna-mbp> (raw)
In-Reply-To: <42af50cc-acde-d7a9-19da-8e2fb87bce48@isovalent.com>

Quentin Monnet <quentin@isovalent.com> [Fri, 2020-02-28 04:51 -0800]:
> 2020-02-26 18:32 UTC-0800 ~ Andrey Ignatov <rdna@fb.com>
> > 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@fb.com>
> > ---
> >   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

      reply	other threads:[~2020-02-28 20:15 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-27  2:32 [PATCH bpf-next] bpf: Add drgn script to list progs/maps Andrey Ignatov
2020-02-27  5:45 ` Song Liu
2020-02-27 17:01   ` Andrey Ignatov
2020-02-27  6:27 ` Andrii Nakryiko
2020-02-27 17:38   ` Andrey Ignatov
2020-02-27 18:01 ` Stanislav Fomichev
2020-02-27 18:26   ` Andrey Ignatov
2020-02-27 21:11     ` Daniel Borkmann
2020-02-27 21:32       ` Daniel Borkmann
2020-02-27 22:19         ` Omar Sandoval
2020-02-28 20:11           ` Andrey Ignatov
2020-02-28 21:29             ` Andrey Ignatov
2020-02-28 12:51 ` Quentin Monnet
2020-02-28 20:15   ` Andrey Ignatov [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200228201514.GB51456@rdna-mbp \
    --to=rdna@fb.com \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=kernel-team@fb.com \
    --cc=osandov@fb.com \
    --cc=quentin@isovalent.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).