Skip to content

Commit

Permalink
Merge branch 'refs/heads/upstream-HEAD' into repo-HEAD
Browse files Browse the repository at this point in the history
  • Loading branch information
Delphix Engineering committed May 31, 2024
2 parents c814c8d + b475670 commit 35bd99e
Show file tree
Hide file tree
Showing 12 changed files with 368 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
run: |
sudo apt-get update -y
sudo apt-get install -y check dwarves libelf-dev libdw-dev qemu-kvm zstd ${{ matrix.cc == 'clang' && 'libomp-$(clang --version | sed -rn "s/.*clang version ([0-9]+).*/\\1/p")-dev' || '' }}
pip install pyroute2 ${USE_PRE_COMMIT/1/pre-commit}
pip install pyroute2 setuptools ${USE_PRE_COMMIT/1/pre-commit}
- name: Generate version.py
run: python setup.py --version
- name: Check with mypy
Expand Down
80 changes: 52 additions & 28 deletions .packit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,39 +25,63 @@ jobs:
owner: "@meta"
project: drgn
targets:
- fedora-all-aarch64
- fedora-all-i386
- fedora-all-ppc64le
- fedora-all-s390x
- fedora-all-x86_64
- fedora-eln-aarch64
- fedora-eln-i386
- fedora-eln-ppc64le
fedora-all-aarch64: {}
fedora-all-i386: {}
fedora-all-ppc64le: {}
fedora-all-s390x: {}
fedora-all-x86_64: {}
fedora-eln-aarch64: {}
fedora-eln-i386: {}
fedora-eln-ppc64le: {}
# Disabled due to fedora-eln/eln#170.
# - fedora-eln-s390x
- fedora-eln-x86_64
- epel-8-aarch64
- epel-8-ppc64le
- epel-8-s390x
- epel-8-x86_64
# fedora-eln-s390x: {}
fedora-eln-x86_64: {}
epel-8-aarch64: {}
epel-8-ppc64le: {}
epel-8-s390x: {}
epel-8-x86_64: {}
centos-stream+epel-next-9-aarch64:
additional_repos:
- https://kojihub.stream.centos.org/kojifiles/repos/c9s-build/latest/aarch64/
centos-stream+epel-next-9-ppc64le:
additional_repos:
- https://kojihub.stream.centos.org/kojifiles/repos/c9s-build/latest/ppc64le/
centos-stream+epel-next-9-s390x:
additional_repos:
- https://kojihub.stream.centos.org/kojifiles/repos/c9s-build/latest/s390x/
centos-stream+epel-next-9-x86_64:
additional_repos:
- https://kojihub.stream.centos.org/kojifiles/repos/c9s-build/latest/x86_64/

- job: copr_build
trigger: pull_request
owner: "@meta"
project: drgn
targets:
- fedora-all-aarch64
- fedora-all-i386
- fedora-all-ppc64le
- fedora-all-s390x
- fedora-all-x86_64
- fedora-eln-aarch64
- fedora-eln-i386
- fedora-eln-ppc64le
fedora-all-aarch64: {}
fedora-all-i386: {}
fedora-all-ppc64le: {}
fedora-all-s390x: {}
fedora-all-x86_64: {}
fedora-eln-aarch64: {}
fedora-eln-i386: {}
fedora-eln-ppc64le: {}
# Disabled due to fedora-eln/eln#170.
# - fedora-eln-s390x
- fedora-eln-x86_64
- epel-8-aarch64
- epel-8-ppc64le
- epel-8-s390x
- epel-8-x86_64
# fedora-eln-s390x: {}
fedora-eln-x86_64: {}
epel-8-aarch64: {}
epel-8-ppc64le: {}
epel-8-s390x: {}
epel-8-x86_64: {}
centos-stream+epel-next-9-aarch64:
additional_repos:
- https://kojihub.stream.centos.org/kojifiles/repos/c9s-build/latest/aarch64/
centos-stream+epel-next-9-ppc64le:
additional_repos:
- https://kojihub.stream.centos.org/kojifiles/repos/c9s-build/latest/ppc64le/
centos-stream+epel-next-9-s390x:
additional_repos:
- https://kojihub.stream.centos.org/kojifiles/repos/c9s-build/latest/s390x/
centos-stream+epel-next-9-x86_64:
additional_repos:
- https://kojihub.stream.centos.org/kojifiles/repos/c9s-build/latest/x86_64/
10 changes: 9 additions & 1 deletion _drgn.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,22 @@ class Program:
:meth:`[] <.__getitem__>` operator.
"""

def __init__(self, platform: Optional[Platform] = None) -> None:
def __init__(
self,
platform: Optional[Platform] = None,
*,
vmcoreinfo: Union[bytes, str, None] = None,
) -> None:
"""
Create a ``Program`` with no target program. It is usually more
convenient to use one of the :ref:`api-program-constructors`.
:param platform: The platform of the program, or ``None`` if it should
be determined automatically when a core dump or symbol file is
added.
:param vmcoreinfo: Optionally provide the ``VMCOREINFO`` note data for
Linux kernel core dumps, which will override any detected data. When
not provided or ``None``, automatically detect the info.
"""
...
flags: ProgramFlags
Expand Down
99 changes: 81 additions & 18 deletions contrib/bpf_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@

"""List BPF programs or maps and their properties unavailable via kernel API."""

import sys
import argparse

from drgn.helpers.common.type import enum_type_to_class
from drgn.helpers.linux import bpf_map_for_each, bpf_prog_for_each, hlist_for_each_entry
from drgn.helpers.linux import (
bpf_map_for_each,
bpf_prog_for_each,
bpf_link_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")
BpfAttachType = enum_type_to_class(prog.type("enum bpf_attach_type"), "BpfAttachType")
BpfLinkType = enum_type_to_class(prog.type("enum bpf_link_type"), "BpfLinkType")


def get_btf_name(btf, btf_id):
Expand All @@ -29,11 +36,11 @@ def get_prog_btf_name(bpf_prog):
return ""


def get_prog_name(bpf_prog):
def get_bpf_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):
def bpf_attach_type_to_tramp(attach_type):
# bpf_tramp_prog_type is available since linux kernel 5.5, this code should
# be called only after checking for bpf_prog.aux.trampoline to be present
# though so no error checking here.
Expand All @@ -52,21 +59,21 @@ def attach_type_to_tramp(attach_type):
return BpfProgTrampType.BPF_TRAMP_REPLACE


def get_linked_func(bpf_prog):
kind = attach_type_to_tramp(bpf_prog.expected_attach_type)
def get_bpf_linked_func(bpf_prog):
kind = bpf_attach_type_to_tramp(bpf_prog.expected_attach_type)

linked_prog = bpf_prog.aux.linked_prog
linked_prog_id = linked_prog.aux.id.value_()
linked_btf_id = bpf_prog.aux.attach_btf_id.value_()
linked_name = (
f"{get_prog_name(linked_prog)}->"
f"{get_bpf_prog_name(linked_prog)}->"
f"{get_btf_name(linked_prog.aux.btf, linked_btf_id)}()"
)

return f"{linked_prog_id}->{linked_btf_id}: {kind.name} {linked_name}"


def get_tramp_progs(bpf_prog):
def get_bpf_tramp_progs(bpf_prog):
try:
tr = bpf_prog.aux.member_("trampoline")
except LookupError:
Expand All @@ -88,20 +95,28 @@ def get_tramp_progs(bpf_prog):
yield tramp_aux.prog


def list_bpf_progs(args):
def inspect_bpf_prog(bpf_prog):
id_ = bpf_prog.aux.id.value_()
type_ = BpfProgType(bpf_prog.type).name
name = get_bpf_prog_name(bpf_prog)

linked = ", ".join([get_bpf_linked_func(p) for p in get_bpf_tramp_progs(bpf_prog)])
if linked:
linked = f" linked:[{linked}]"

return f"{id_:>6}: {type_:32} {name:32} {linked}"


def list_bpf_progs():
for bpf_prog in bpf_prog_for_each(prog):
id_ = bpf_prog.aux.id.value_()
type_ = BpfProgType(bpf_prog.type).name
name = get_prog_name(bpf_prog)
print(inspect_bpf_prog(bpf_prog))

linked = ", ".join([get_linked_func(p) for p in get_tramp_progs(bpf_prog)])
if linked:
linked = f" linked:[{linked}]"

print(f"{id_:>6}: {type_:32} {name:32} {linked}")
def __list_bpf_progs(args):
list_bpf_progs()


def list_bpf_maps(args):
def list_bpf_maps():
for map_ in bpf_map_for_each(prog):
id_ = map_.id.value_()
type_ = BpfMapType(map_.map_type).name
Expand All @@ -110,6 +125,46 @@ def list_bpf_maps(args):
print(f"{id_:>6}: {type_:32} {name}")


def __list_bpf_maps(args):
list_bpf_maps()


def list_bpf_links():
for link in bpf_link_for_each(prog):
id_ = link.id.value_()
type_ = BpfLinkType(link.type).name
prog_ = inspect_bpf_prog(link.prog)

print(f"{id_:>6}: {type_:32} prog: {prog_}")


def __list_bpf_links(args):
list_bpf_links()


def __run_interactive(args):
try:
from drgn.cli import run_interactive
except ImportError:
sys.exit("Interactive mode requires drgn 0.0.23+")

def should_add_to_globals(name):
if name.startswith("__"):
return False
return "bpf" in name or "Bpf" in name or "btf" in name

globals_keys = globals().keys()

def globals_func(globals_):
for key in globals_keys:
if should_add_to_globals(key):
globals_[key] = globals()[key]

return globals_

run_interactive(prog, globals_func=globals_func)


def main():
parser = argparse.ArgumentParser(
description="drgn script to list BPF programs or maps and their properties unavailable via kernel API"
Expand All @@ -119,10 +174,18 @@ def main():
subparsers.required = True

prog_parser = subparsers.add_parser("prog", aliases=["p"], help="list BPF programs")
prog_parser.set_defaults(func=list_bpf_progs)
prog_parser.set_defaults(func=__list_bpf_progs)

map_parser = subparsers.add_parser("map", aliases=["m"], help="list BPF maps")
map_parser.set_defaults(func=list_bpf_maps)
map_parser.set_defaults(func=__list_bpf_maps)

link_parser = subparsers.add_parser("link", aliases=["l"], help="list BPF links")
link_parser.set_defaults(func=__list_bpf_links)

interact_parser = subparsers.add_parser(
"interact", aliases=["i"], help="start interactive shell, requires 0.0.23+ drgn"
)
interact_parser.set_defaults(func=__run_interactive)

args = parser.parse_args()
args.func(args)
Expand Down
19 changes: 17 additions & 2 deletions contrib/lsmod.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,26 @@

from drgn.helpers.linux.list import list_for_each_entry

def module_total_size(mod):
# Since Linux kernel commit ac3b43283923 ("module: replace module_layout
# with module_memory") (in v6.4), the memory sizes are in the struct
# module::mem array. Before that, they are in struct module::init_layout
# and struct module::core_layout.
try:
num_types = mod.prog_["MOD_MEM_NUM_TYPES"]
except KeyError:
return (mod.init_layout.size + mod.core_layout.size).value_()
else:
return sum(
mod.mem[type].size.value_()
for type in range(num_types)
)


print("Module Size Used by")
config_module_unload = prog.type("struct module").has_member("refcnt")
for mod in list_for_each_entry("struct module", prog["modules"].address_of_(), "list"):
name = mod.name.string_().decode()
size = (mod.init_layout.size + mod.core_layout.size).value_()
if config_module_unload:
refcnt = mod.refcnt.counter.value_() - 1
used_by = [
Expand All @@ -26,4 +41,4 @@
used = ",".join(used_by)
if used:
used = " " + used
print(f"{name:19} {size:>8} {refcnt}{used}")
print(f"{name:19} {module_total_size(mod):>8} {refcnt}{used}")
25 changes: 24 additions & 1 deletion drgn/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,20 @@ def _main() -> None:
help="don't load any debugging symbols that were not explicitly added with -s",
)

advanced_group = parser.add_argument_group("advanced")
advanced_group.add_argument(
"--architecture",
metavar="ARCH",
choices=[a.name for a in drgn.Architecture]
+ [a.name.lower() for a in drgn.Architecture],
help="set the program architecture, in case it can't be auto-detected",
)
advanced_group.add_argument(
"--vmcoreinfo",
type=str,
metavar="PATH",
help="path to vmcoreinfo file (overrides any already present in the file)",
)
parser.add_argument(
"--log-level",
choices=["debug", "info", "warning", "error", "critical", "none"],
Expand Down Expand Up @@ -258,7 +272,16 @@ def _main() -> None:
else:
logger.setLevel(args.log_level.upper())

prog = drgn.Program()
platform = None
if args.architecture:
platform = drgn.Platform(drgn.Architecture[args.architecture.upper()])

vmcoreinfo = None
if args.vmcoreinfo is not None:
with open(args.vmcoreinfo, "rb") as f:
vmcoreinfo = f.read()

prog = drgn.Program(platform=platform, vmcoreinfo=vmcoreinfo)
try:
if args.core is not None:
prog.set_core_dump(args.core)
Expand Down
17 changes: 16 additions & 1 deletion libdrgn/arch_x86_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@

#include "arch_x86_64_defs.inc"

// This is __START_KERNEL_map from the Linux kernel. It has never been modified
// since the x86_64 architecture was introduced.
#define START_KERNEL_MAP UINT64_C(0xffffffff80000000)

static const struct drgn_cfi_row default_dwarf_cfi_row_x86_64 = DRGN_CFI_ROW(
// The System V psABI defines the CFA as the value of rsp in the calling
// frame.
Expand Down Expand Up @@ -616,7 +620,18 @@ linux_kernel_pgtable_iterator_next_x86_64(struct drgn_program *prog,
for (;; level--) {
uint64_t table;
bool table_physical;
if (level == levels) {
if (level == levels && prog->vmcoreinfo.phys_base &&
it->it.pgtable == prog->vmcoreinfo.swapper_pg_dir) {
// Avoid recursive address translation on swapper_pg_dir by
// directly resolving to a physical address. Don't do
// this if phys_base is 0, since that likely means it
// was not present in the vmcoreinfo. It has been
// present since Linux kernel commit 401721ecd1dc
// ("kexec: export the value of phys_base instead of
// symbol address") (in v4.10).
table = it->it.pgtable + prog->vmcoreinfo.phys_base - START_KERNEL_MAP;
table_physical = true;
} else if (level == levels) {
table = it->it.pgtable;
table_physical = false;
} else {
Expand Down
Loading

0 comments on commit 35bd99e

Please sign in to comment.