├── .editorconfig ├── .flake8 ├── .git-blame-ignore-revs ├── .github └── workflows │ ├── ci.yml │ ├── dco-check.yml │ ├── pull_request.yml │ └── vmtest-build.yml ├── .gitignore ├── .packit.yaml ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CONTRIBUTING.rst ├── COPYING ├── LICENSES ├── GPL-2.0-or-later.txt ├── LGPL-2.1-or-later.txt └── MIT.txt ├── MANIFEST.in ├── README.rst ├── _drgn.pyi ├── _drgn_util ├── __init__.py ├── elf.py ├── platform.py └── plugins.py ├── contrib ├── README.rst ├── bpf_inspect.py ├── btrfs_orphan_subvolumes.py ├── btrfs_print_fs_uuids_cache.py ├── btrfs_tree.py ├── btrfs_tree_mod_log.py ├── cgroup.py ├── dm_crypt_key.py ├── dump_btrfs_bgs.py ├── find_struct_file.py ├── fs_inodes.py ├── gcore.py ├── irq.py ├── kcore_list.py ├── kernel_sys.py ├── lsmod.py ├── mount.py ├── negdentdelete.py ├── platform_drivers.py ├── pp_leak │ ├── README.rst │ ├── guess_leaky_skbs.py │ ├── ls_pp_leaks.py │ └── scan_tcp_socks.py ├── ps.py ├── ptdrgn.py ├── search_kernel_memory.py ├── slabinfo.py ├── stack_trace_call_fault.py ├── tcp_sock.py ├── vmallocinfo.py ├── vmmap.py └── vmstat.py ├── docs ├── _static │ ├── custom.css │ └── logo.png ├── advanced_usage.rst ├── api_reference.rst ├── case_studies.rst ├── case_studies │ ├── dm_crypt_key.rst │ └── kyber_stack_trace.rst ├── conf.py ├── exts │ ├── details.py │ ├── drgndoc │ │ ├── __init__.py │ │ ├── docstrings.py │ │ ├── ext.py │ │ ├── format.py │ │ ├── namespace.py │ │ ├── parse.py │ │ ├── util.py │ │ └── visitor.py │ ├── linuxsrc.py │ └── setuptools_config.py ├── favicon.ico ├── getting_debugging_symbols.rst ├── helpers.rst ├── index.rst ├── installation.rst ├── man │ └── drgn.rst ├── release_highlights.rst ├── release_highlights │ ├── 0.0.22.rst │ ├── 0.0.23.rst │ ├── 0.0.24.rst │ ├── 0.0.25.rst │ ├── 0.0.26.rst │ ├── 0.0.27.rst │ ├── 0.0.28.rst │ ├── 0.0.30.rst │ └── 0.0.31.rst ├── requirements.txt ├── support_matrix.rst ├── tutorials.rst ├── tutorials │ └── blk_rq_qos_crash.rst └── user_guide.rst ├── drgn ├── __init__.py ├── __main__.py ├── cli.py ├── helpers │ ├── __init__.py │ ├── common │ │ ├── __init__.py │ │ ├── format.py │ │ ├── memory.py │ │ ├── prog.py │ │ ├── stack.py │ │ └── type.py │ ├── experimental │ │ ├── __init__.py │ │ └── kmodify.py │ └── linux │ │ ├── __init__.py │ │ ├── bitmap.py │ │ ├── bitops.py │ │ ├── block.py │ │ ├── boot.py │ │ ├── bpf.py │ │ ├── cgroup.py │ │ ├── cpumask.py │ │ ├── device.py │ │ ├── fs.py │ │ ├── idr.py │ │ ├── kallsyms.py │ │ ├── kconfig.py │ │ ├── kernfs.py │ │ ├── kthread.py │ │ ├── list.py │ │ ├── list_nulls.py │ │ ├── llist.py │ │ ├── mapletree.py │ │ ├── mm.py │ │ ├── module.py │ │ ├── net.py │ │ ├── nodemask.py │ │ ├── percpu.py │ │ ├── pid.py │ │ ├── plist.py │ │ ├── printk.py │ │ ├── radixtree.py │ │ ├── rbtree.py │ │ ├── sched.py │ │ ├── slab.py │ │ ├── stackdepot.py │ │ ├── tc.py │ │ ├── tcp.py │ │ ├── timekeeping.py │ │ ├── user.py │ │ ├── wait.py │ │ └── xarray.py ├── internal │ ├── __init__.py │ ├── repl.py │ ├── rlcompleter.py │ └── sudohelper.py └── py.typed ├── libdrgn ├── .gitignore ├── Doxyfile ├── Makefile.am ├── accessors.c ├── arch_aarch64.c ├── arch_aarch64_defs.py ├── arch_arm.c ├── arch_arm_defs.py ├── arch_i386.c ├── arch_ppc64.c ├── arch_ppc64_defs.py ├── arch_riscv.c ├── arch_s390x.c ├── arch_s390x_defs.py ├── arch_x86_64.c ├── arch_x86_64_defs.py ├── array.h ├── binary_buffer.c ├── binary_buffer.h ├── binary_search.h ├── binary_search_tree.h ├── bitops.h ├── build-aux │ ├── .gitignore │ ├── checkmk │ ├── codegen_utils.py │ ├── gen_arch_inc_strswitch.py │ ├── gen_c_keywords_inc_strswitch.py │ ├── gen_constants.py │ ├── gen_elf_sections.py │ └── gen_strswitch.py ├── c_lexer.h ├── cfi.c ├── cfi.h ├── cityhash.h ├── cleanup.h ├── configure.ac ├── crc32.c ├── crc32.h ├── debug_info.c ├── debug_info.h ├── debug_info_options.c ├── debug_info_options.h ├── drgn.h ├── drgn_internal.h ├── drgn_program_parse_vmcoreinfo.inc.strswitch ├── dwarf_constants.c ├── dwarf_constants.h ├── dwarf_info.c ├── dwarf_info.h ├── elf_file.c ├── elf_file.h ├── elf_notes.c ├── elf_notes.h ├── elf_symtab.c ├── elf_symtab.h ├── error.c ├── error.h ├── examples │ └── load_debug_info.c ├── generics.h ├── handler.c ├── handler.h ├── hash_table.c ├── hash_table.h ├── helpers.h ├── hexlify.c ├── hexlify.h ├── include │ └── elf.h ├── io.c ├── io.h ├── kallsyms.c ├── kallsyms.h ├── kdump.c ├── language.c ├── language.h ├── language_c.c ├── lazy_object.c ├── lazy_object.h ├── lexer.c ├── lexer.h ├── linux_kernel.c ├── linux_kernel.h ├── linux_kernel_helpers.c ├── linux_kernel_object_find.inc.strswitch ├── log.c ├── log.h ├── m4 │ ├── .gitignore │ ├── ax_append_compile_flags.m4 │ ├── ax_append_flag.m4 │ ├── ax_check_compile_flag.m4 │ ├── ax_require_defined.m4 │ ├── my_c_auto.m4 │ ├── my_c_switch_enum.m4 │ ├── my_check_va_args_comma_deletion.m4 │ └── my_python_devel.m4 ├── memory_reader.c ├── memory_reader.h ├── minmax.h ├── no_python.c ├── nstring.h ├── object.c ├── object.h ├── openmp.c ├── openmp.h ├── orc.h ├── orc_info.c ├── orc_info.h ├── path.c ├── path.h ├── platform.c ├── platform.h ├── plugins.h ├── pp.h ├── program.c ├── program.h ├── python │ ├── debug_info_options.c │ ├── drgnpy.h │ ├── error.c │ ├── helpers.c │ ├── language.c │ ├── main.c │ ├── module.c │ ├── module_section_addresses.c │ ├── object.c │ ├── platform.c │ ├── plugins.c │ ├── program.c │ ├── stack_trace.c │ ├── symbol.c │ ├── symbol_index.c │ ├── test.c │ ├── thread.c │ ├── type.c │ ├── type_kind_set.c │ └── util.c ├── register_state.c ├── register_state.h ├── serialize.c ├── serialize.h ├── splay_tree.c ├── stack_trace.c ├── stack_trace.h ├── string_builder.c ├── string_builder.h ├── symbol.c ├── symbol.h ├── tests │ ├── binary_search.c.in │ ├── cityhash.c.in │ ├── crc32.c.in │ ├── hexlify.c.in │ ├── language_c.c.in │ ├── lexer.c.in │ ├── path.c.in │ ├── recursion_guard.c.in │ ├── serialize.c.in │ └── test_util.h ├── type.c ├── type.h ├── util.c ├── util.h └── vector.h ├── pyproject.toml ├── pytest.ini ├── scripts ├── build_dists.sh ├── build_manylinux_in_docker.sh ├── crashme │ ├── Makefile │ ├── common.c │ ├── crashme.c │ ├── crashme.h │ └── main.c ├── cscope.sh ├── gen_dwarf_constants.py ├── gen_elf_compat.py ├── gen_elf_py.py ├── generate_page_flag_getters.py ├── generate_primitive_type_spellings.py ├── iwyu.py ├── pp │ ├── gen_pp_cat.py │ └── gen_pp_map.py └── scalar_alignment.c ├── setup.py ├── tests ├── __init__.py ├── assembler.py ├── dwarf.py ├── dwarfwriter.py ├── elfwriter.py ├── helpers │ ├── __init__.py │ └── common │ │ ├── __init__.py │ │ ├── test_format.py │ │ ├── test_prog.py │ │ └── test_type.py ├── libdrgn.py ├── linux_kernel │ ├── __init__.py │ ├── bpf.py │ ├── helpers │ │ ├── __init__.py │ │ ├── test_bitops.py │ │ ├── test_block.py │ │ ├── test_boot.py │ │ ├── test_bpf.py │ │ ├── test_cgroup.py │ │ ├── test_common.py │ │ ├── test_cpumask.py │ │ ├── test_fs.py │ │ ├── test_idr.py │ │ ├── test_kallsyms.py │ │ ├── test_kconfig.py │ │ ├── test_kernfs.py │ │ ├── test_kmodify.py │ │ ├── test_kthread.py │ │ ├── test_list.py │ │ ├── test_llist.py │ │ ├── test_mapletree.py │ │ ├── test_mm.py │ │ ├── test_module.py │ │ ├── test_net.py │ │ ├── test_nodemask.py │ │ ├── test_percpu.py │ │ ├── test_pid.py │ │ ├── test_plist.py │ │ ├── test_printk.py │ │ ├── test_radixtree.py │ │ ├── test_rbtree.py │ │ ├── test_sched.py │ │ ├── test_slab.py │ │ ├── test_stackdepot.py │ │ ├── test_tc.py │ │ ├── test_tcp.py │ │ ├── test_timekeeping.py │ │ ├── test_user.py │ │ ├── test_wait.py │ │ └── test_xarray.py │ ├── kmod │ │ ├── Makefile │ │ └── drgn_test.c │ ├── test_debug_info.py │ ├── test_special_objects.py │ ├── test_stack_trace.py │ ├── test_symbol.py │ ├── test_threads.py │ ├── tools │ │ ├── __init__.py │ │ └── test_fsrefs.py │ └── vmcore │ │ ├── __init__.py │ │ └── test_vmcore.py ├── resources │ ├── .gitignore │ ├── __init__.py │ ├── __main__.py │ ├── crashme.alt.zst │ ├── crashme.core.zst │ ├── crashme.dwz.zst │ ├── crashme.so.dwz.zst │ ├── crashme.so.zst │ ├── crashme.zst │ ├── crashme_pie.core.zst │ ├── crashme_pie.zst │ ├── crashme_pie_no_headers.core.zst │ ├── crashme_static.core.zst │ ├── crashme_static.zst │ ├── crashme_static_pie.core.zst │ ├── crashme_static_pie.zst │ └── multithreaded.core.zst ├── test_cli.py ├── test_debug_info.py ├── test_debug_info_options.py ├── test_docs.py ├── test_dwarf.py ├── test_filename_matches.py ├── test_language_c.py ├── test_language_cpp.py ├── test_logging.py ├── test_module.py ├── test_object.py ├── test_platform.py ├── test_plugins.py ├── test_program.py ├── test_python.py ├── test_serialize.py ├── test_stack_trace.py ├── test_symbol.py ├── test_thread.py ├── test_type.py ├── test_type_kind_set.py └── test_util.py ├── tools ├── __init__.py └── fsrefs.py ├── util.py └── vmtest ├── README.rst ├── __init__.py ├── __main__.py ├── asynciosubprocess.py ├── config.py ├── download.py ├── enter_kdump.py ├── githubapi.py ├── kbuild.py ├── kmod.py ├── manage.py ├── onoatimehack.c ├── patches ├── 4.14-arm64-build-Remove-.eh_frame-sections-due-to-unwind-.patch ├── 4.19-arm64-build-Remove-.eh_frame-sections-due-to-unwind-.patch ├── 4.9-arm64-build-Remove-.eh_frame-sections-due-to-unwind-.patch ├── 5.10-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch ├── 5.10-gcc-12-disable-Wdangling-pointer-warning-for-now.patch ├── 5.10-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch ├── 5.10-kbuild-skip-per-CPU-BTF-generation-for-pahole-v1.18-.patch ├── 5.11-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch ├── 5.11-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch ├── 5.12-kbuild-Quote-OBJCOPY-var-to-avoid-a-pahole-call-brea.patch ├── 5.15-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch ├── 5.15-kbuild-Unify-options-for-BTF-generation-for-vmlinux.patch ├── 5.17-page_pool-enable-CONFIG_PAGE_POOL-by-default.patch ├── 5.18-Revert-Makefile-link-with-z-noexecstack-no-warn-rwx-.patch ├── 5.4-arm64-build-Remove-.eh_frame-sections-due-to-unwind-.patch ├── 5.4-page_pool-enable-CONFIG_PAGE_POOL-by-default.patch ├── filelock-fix-name-of-file_lease-slab-cache.patch ├── gcc-12-disable-Wdangling-pointer-warning-for-now.patch ├── kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch ├── kernel-reboot-emergency_restart-Set-correct-system_s.patch ├── lib-raid6-add-option-to-skip-algo-benchmarking.patch ├── libsubcmd-Fix-use-after-free-for-realloc-.-0.patch ├── powerpc-pseries-Fix-hcall-tracepoints-with-JUMP_LABE.patch ├── proc-kcore-allow-enabling-CONFIG_PROC_KCORE-on-ARM.patch ├── s390-crash-fix-proc-vmcore-reads.patch ├── s390-kernel-emit-CFI-data-in-.debug_frame-and-discar.patch ├── s390-mm-make-memory_block_size_bytes-available-for-M.patch └── sched-work-around-mystery-QEMU-hang.patch ├── rootfsbuild.py └── vm.py /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | trim_trailing_whitespace = true 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 8 8 | 9 | [*.{py,pyi}] 10 | indent_style = space 11 | indent_size = 4 12 | 13 | [*.rst] 14 | indent_style = space 15 | indent_size = 4 16 | 17 | [{makefile, Makefile}*] 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | extend-ignore = 3 | # "undefined name": leave this to mypy. 4 | F821, 5 | 6 | # These get confused by the C code we have embedded in docstrings in 7 | # various places. 8 | # "indentation contains mixed spaces and tabs" 9 | E101, 10 | # "indentation contains tabs" 11 | W191, 12 | 13 | # For the following, we live by Black. 14 | # "whitespace before ':'" 15 | E203, 16 | # "line too long" 17 | E501, 18 | # "line break before binary operator" 19 | W503 20 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | 660276a0b84fc5b8a7287d5b7a3b49d784115077 2 | -------------------------------------------------------------------------------- /.github/workflows/dco-check.yml: -------------------------------------------------------------------------------- 1 | name: DCO Check 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened, ready_for_review] 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | check: 13 | if: ${{ !github.event.pull_request.draft }} 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout commit logs 17 | run: | 18 | git init 19 | git fetch --filter=blob:none "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" "$GITHUB_BASE_REF" "$GITHUB_REF" 20 | - name: Check for DCO sign-offs 21 | shell: bash 22 | run: | 23 | status=0 24 | while read -r commit; do 25 | author="$(git show --no-patch --pretty='format:%an <%ae>' "$commit")" 26 | if ! git show --no-patch --pretty='format:%(trailers:key=Signed-off-by,valueonly)' "$commit" | grep -Fxq "$author"; then 27 | if [ $status -eq 0 ]; then 28 | echo "The following commits are missing a Developer Certificate of Origin sign-off;" 29 | echo "see https://github.com/osandov/drgn/blob/main/CONTRIBUTING.rst#signing-off" 30 | echo 31 | fi 32 | status=1 33 | git show --no-patch "$commit" 34 | fi 35 | done < <(git rev-list --no-merges "FETCH_HEAD..$GITHUB_SHA") 36 | if [ $status -eq 0 ]; then 37 | echo "All commits have a Developer Certificate of Origin sign-off" 38 | fi 39 | exit $status 40 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request CI 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - synchronize 8 | - reopened 9 | - labeled 10 | 11 | jobs: 12 | test: 13 | uses: ./.github/workflows/ci.yml 14 | if: ${{ github.event.action != 'labeled' || github.event.label.name == 'test-all-python-versions' }} 15 | with: 16 | test_all_python_versions: ${{ contains(github.event.pull_request.labels.*.name, 'test-all-python-versions') }} 17 | test_all_kernel_flavors: ${{ contains(github.event.pull_request.labels.*.name, 'test-all-kernel-flavors') }} 18 | -------------------------------------------------------------------------------- /.github/workflows/vmtest-build.yml: -------------------------------------------------------------------------------- 1 | name: vmtest Build 2 | 3 | on: 4 | schedule: 5 | - cron: '16 6 * * MON' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | strategy: 11 | matrix: 12 | flavor: [default, alternative, tiny] 13 | arch: [x86_64, aarch64, ppc64, s390x, arm] 14 | fail-fast: false 15 | max-parallel: 5 16 | runs-on: ubuntu-22.04 17 | permissions: 18 | contents: write 19 | env: 20 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Install dependencies 24 | run: | 25 | sudo apt-get update 26 | sudo apt-get install dwarves libelf-dev 27 | pip install aiohttp uritemplate 28 | - name: Build and upload ${{ matrix.arch }} ${{ matrix.flavor }} kernels 29 | run: python3 -m vmtest.manage --kernel-directory build/vmtest/linux.git --build-directory build/vmtest/kbuild -K -a ${{ matrix.arch }} -f ${{ matrix.flavor }} 30 | - name: Upload kernel build logs 31 | if: always() 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: kernel-build-logs-${{ matrix.arch }}-${{ matrix.flavor }} 35 | path: build/vmtest/kbuild/*.log 36 | if-no-files-found: ignore 37 | test: 38 | needs: build 39 | uses: ./.github/workflows/ci.yml 40 | with: 41 | test_all_python_versions: true 42 | test_all_kernel_flavors: true 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.so 3 | /.coverage 4 | /.mypy_cache 5 | /build 6 | /coverage.info 7 | /cscope.* 8 | /dist 9 | /docs/_build 10 | /drgn-*.tar.gz 11 | /drgn.egg-info 12 | /drgn/internal/version.py 13 | /htmlcov 14 | /python-drgn-*.src.rpm 15 | /python-drgn.spec 16 | __pycache__ 17 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: ^contrib/ 2 | repos: 3 | - repo: https://github.com/pycqa/isort 4 | rev: 5.13.2 5 | hooks: 6 | - id: isort 7 | name: isort (python) 8 | - repo: https://github.com/psf/black 9 | rev: 24.8.0 10 | hooks: 11 | - id: black 12 | exclude: ^docs/exts/details\.py$ 13 | - repo: https://github.com/pycqa/flake8 14 | rev: 7.1.2 15 | hooks: 16 | - id: flake8 17 | - repo: https://github.com/pre-commit/mirrors-mypy 18 | rev: v1.14.1 19 | hooks: 20 | - id: mypy 21 | args: [--show-error-codes, --strict, --no-warn-return-any] 22 | files: ^(drgn/.*\.py|_drgn.pyi|_drgn_util/.*\.py|tools/.*\.py)$ 23 | - repo: https://github.com/pre-commit/pre-commit-hooks 24 | rev: v5.0.0 25 | hooks: 26 | - id: trailing-whitespace 27 | exclude_types: [diff] 28 | - id: end-of-file-fixer 29 | exclude_types: [diff] 30 | - id: check-yaml 31 | - id: check-added-large-files 32 | - id: debug-statements 33 | - id: check-merge-conflict 34 | - repo: https://github.com/netromdk/vermin 35 | rev: v1.6.0 36 | hooks: 37 | - id: vermin 38 | args: ['-t=3.8-', '--violations', '--eval-annotations'] 39 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | build: 3 | os: ubuntu-22.04 4 | tools: 5 | python: "3" 6 | apt_packages: 7 | - graphviz 8 | sphinx: 9 | configuration: docs/conf.py 10 | python: 11 | install: 12 | - requirements: docs/requirements.txt 13 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | drgn is provided under: 2 | 3 | SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | Some source files are provided under different licenses as noted in each file. 6 | See the LICENSES directory for the full list of licenses used. 7 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include docs *.css *.ico *.png *.py *.rst 2 | recursive-include tests *.py 3 | recursive-include tests/resources *.zst 4 | include tests/linux_kernel/kmod/Makefile tests/linux_kernel/kmod/drgn_test.c 5 | recursive-include contrib *.py *.rst 6 | recursive-include tools *.py *.rst 7 | recursive-include vmtest *.c *.py *.rst 8 | recursive-include vmtest/patches *.patch 9 | recursive-include LICENSES *.txt 10 | include .flake8 CONTRIBUTING.rst COPYING pytest.ini util.py 11 | -------------------------------------------------------------------------------- /_drgn_util/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Internal utilities for drgn 6 | 7 | This package contains utilities shared between the drgn package and supporting 8 | build/test code. You should not use them. 9 | 10 | This package must not depend on the drgn package itself since it is used before 11 | the _drgn extension module is built. 12 | """ 13 | -------------------------------------------------------------------------------- /contrib/README.rst: -------------------------------------------------------------------------------- 1 | Community-Contributed Content 2 | ============================= 3 | 4 | This directory contains drgn scripts, libraries, and notes that have been 5 | contributed by the community but aren't considered a part of drgn proper. Code 6 | in this directory is not tested and not necessarily up to the rest of the 7 | project's standards. 8 | 9 | This is intended as a central location to share drgn ideas with a low barrier 10 | to entry. If you have time to polish your code, consider submitting it as a 11 | proper helper or tool. If not, feel free to dump it here. Someone else might 12 | find it useful as a starting point for their own investigation. It could even 13 | be adapted into a helper or tool later. 14 | 15 | Contributing to ``contrib`` 16 | --------------------------- 17 | 18 | The bar for contributing to ``contrib`` is intentionally low. Code submitted 19 | here can be rough and will be only lightly reviewed. The only hard requirements 20 | are: 21 | 22 | * It must be relevant to drgn. 23 | * All files must have a comment or docstring at the top describing what they 24 | are. This can be short. 25 | 26 | There are also some boring legal requirements: 27 | 28 | * All files must have a copyright notice. 29 | * All files must be licensed under the LGPLv2.1+ (using 30 | ``SPDX-License-Identifier: LGPL-2.1-or-later``). 31 | * All commits must have a ``Signed-off-by`` trailer. See `Signing Off 32 | <../CONTRIBUTING.rst#signing-off>`_. 33 | 34 | We may choose to edit, reorganize, or drop parts your contribution. If in 35 | doubt, go ahead and open a pull request, and we'll decide what to do with it. 36 | -------------------------------------------------------------------------------- /contrib/btrfs_orphan_subvolumes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | """Dump Btrfs subvolumes that have been deleted but not cleaned up.""" 6 | 7 | from drgn import Object, cast 8 | from drgn.helpers.linux.fs import inode_path, path_lookup 9 | from drgn.helpers.linux.radixtree import radix_tree_for_each 10 | from drgn.helpers.linux.rbtree import rbtree_inorder_for_each_entry 11 | 12 | 13 | def dump_orphan_subvolumes(fs_info: Object) -> None: 14 | prog = fs_info.prog_ 15 | BTRFS_ROOT_ORPHAN_ITEM_INSERTED = prog["BTRFS_ROOT_ORPHAN_ITEM_INSERTED"] 16 | for objectid, entry in radix_tree_for_each(fs_info.fs_roots_radix): 17 | root = cast("struct btrfs_root *", entry) 18 | if root.state & (1 << BTRFS_ROOT_ORPHAN_ITEM_INSERTED): 19 | print(f"orphan root {objectid} has the following inodes in memory:") 20 | for inode in rbtree_inorder_for_each_entry( 21 | "struct btrfs_inode", root.inode_tree.address_of_(), "rb_node" 22 | ): 23 | path = inode_path(inode.vfs_inode.address_of_()) 24 | if path is None: 25 | print(f" inode {inode.vfs_inode.i_ino.value_()} with no cached names") 26 | else: 27 | print(f" {path.decode()}") 28 | 29 | 30 | if __name__ == "__main__": 31 | import argparse 32 | from pathlib import Path 33 | 34 | parser = argparse.ArgumentParser() 35 | parser.add_argument("path", type=Path) 36 | args = parser.parse_args() 37 | 38 | dump_orphan_subvolumes( 39 | cast( 40 | "struct btrfs_fs_info *", 41 | path_lookup(prog, args.path.resolve()).mnt.mnt_sb.s_fs_info, 42 | ) 43 | ) 44 | -------------------------------------------------------------------------------- /contrib/find_struct_file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | """Print what is using a struct file *, given as an address.""" 6 | 7 | import os 8 | import sys 9 | 10 | from drgn import Object 11 | from drgn.helpers.linux.fs import for_each_file 12 | from drgn.helpers.linux.list import list_for_each_entry 13 | from drgn.helpers.linux.pid import for_each_task 14 | 15 | 16 | def find_struct_file_fds(file: Object) -> None: 17 | for task in for_each_task(file.prog_): 18 | for fd, fd_file in for_each_file(task): 19 | if fd_file == file: 20 | print( 21 | f"PID {task.pid.value_()} COMM {task.comm.string_().decode()} FD {fd}" 22 | ) 23 | 24 | 25 | def find_struct_file_binfmt_misc(file: Object) -> None: 26 | prog = file.prog_ 27 | for node in list_for_each_entry( 28 | prog.type("Node", filename="binfmt_misc.c"), 29 | prog.object("entries", filename="binfmt_misc.c").address_of_(), 30 | "list", 31 | ): 32 | if node.interp_file == file: 33 | print(f"binfmt_misc {os.fsdecode(node.name.string_())}") 34 | 35 | 36 | def find_struct_file(file: Object) -> None: 37 | find_struct_file_fds(file) 38 | find_struct_file_binfmt_misc(file) 39 | 40 | 41 | if __name__ == "__main__": 42 | find_struct_file(Object(prog, "struct file *", int(sys.argv[1], 0))) 43 | -------------------------------------------------------------------------------- /contrib/fs_inodes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | """List the paths of all inodes cached in a given filesystem""" 6 | 7 | import os 8 | import sys 9 | 10 | from drgn.helpers.linux.fs import for_each_mount, inode_path 11 | from drgn.helpers.linux.list import list_for_each_entry 12 | 13 | if len(sys.argv) == 1: 14 | path = "/" 15 | else: 16 | path = sys.argv[1] 17 | 18 | mnt = None 19 | for mnt in for_each_mount(prog, dst=path): 20 | pass 21 | if mnt is None: 22 | sys.exit(f"No filesystem mounted at {path}") 23 | 24 | sb = mnt.mnt.mnt_sb 25 | 26 | for inode in list_for_each_entry( 27 | "struct inode", sb.s_inodes.address_of_(), "i_sb_list" 28 | ): 29 | try: 30 | print(os.fsdecode(inode_path(inode))) 31 | except (TypeError, ValueError): 32 | continue 33 | -------------------------------------------------------------------------------- /contrib/kcore_list.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | """Dump the list of memory regions exposed by /proc/kcore.""" 6 | 7 | from drgn import cast 8 | from drgn.helpers.linux.list import list_for_each_entry 9 | 10 | kcore_type = prog.type("enum kcore_type") 11 | for entry in list_for_each_entry( 12 | "struct kcore_list", prog["kclist_head"].address_of_(), "list" 13 | ): 14 | print( 15 | f"{cast(kcore_type, entry.type).format_(type_name=False)} {hex(entry.addr)} {hex(entry.size)}" 16 | ) 17 | -------------------------------------------------------------------------------- /contrib/kernel_sys.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | # Copyright (c) SUSE Linux. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | """Display system information and configuration data.""" 6 | 7 | from datetime import datetime 8 | from datetime import timedelta 9 | 10 | from drgn.helpers.common.format import number_in_binary_units 11 | from drgn.helpers.linux import for_each_online_cpu 12 | from drgn.helpers.linux.mm import totalram_pages 13 | from drgn.helpers.linux.pid import for_each_task 14 | from drgn.helpers.linux.sched import loadavg 15 | 16 | 17 | def print_line(key, value): 18 | print(f"{key:<16} {value}") 19 | 20 | 21 | uts = prog["init_uts_ns"].name 22 | 23 | timekeeper = prog["shadow_timekeeper"] 24 | date = datetime.fromtimestamp(timekeeper.xtime_sec).strftime("%c") 25 | uptime = timedelta(seconds=timekeeper.ktime_sec.value_()) 26 | load = ", ".join([f"{v:.2f}" for v in loadavg(prog)]) 27 | totalram = (prog['PAGE_SIZE'] * totalram_pages(prog)).value_() 28 | 29 | 30 | print_line("CPUS", len(list(for_each_online_cpu(prog)))) 31 | print_line("DATE", date) 32 | print_line("UPTIME", uptime) 33 | print_line("LOAD AVERAGE", load) 34 | print_line("TASKS", len(list(for_each_task(prog)))) 35 | print_line("NODENAME", uts.nodename.string_().decode()) 36 | print_line("RELEASE", uts.release.string_().decode()) 37 | print_line("VERSION", uts.version.string_().decode()) 38 | print_line("MACHINE", uts.machine.string_().decode()) 39 | print_line("MEMORY", number_in_binary_units(totalram)) 40 | -------------------------------------------------------------------------------- /contrib/lsmod.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | """An implementation of lsmod(8) using drgn""" 6 | 7 | from drgn.helpers.linux.list import list_for_each_entry 8 | 9 | def module_total_size(mod): 10 | # Since Linux kernel commit ac3b43283923 ("module: replace module_layout 11 | # with module_memory") (in v6.4), the memory sizes are in the struct 12 | # module::mem array. Before that, they are in struct module::init_layout 13 | # and struct module::core_layout. 14 | try: 15 | num_types = mod.prog_["MOD_MEM_NUM_TYPES"] 16 | except KeyError: 17 | return (mod.init_layout.size + mod.core_layout.size).value_() 18 | else: 19 | return sum( 20 | mod.mem[type].size.value_() 21 | for type in range(num_types) 22 | ) 23 | 24 | 25 | print("Module Size Used by") 26 | config_module_unload = prog.type("struct module").has_member("refcnt") 27 | for mod in list_for_each_entry("struct module", prog["modules"].address_of_(), "list"): 28 | name = mod.name.string_().decode() 29 | if config_module_unload: 30 | refcnt = mod.refcnt.counter.value_() - 1 31 | used_by = [ 32 | use.source.name.string_().decode() 33 | for use in list_for_each_entry( 34 | "struct module_use", mod.source_list.address_of_(), "source_list" 35 | ) 36 | ] 37 | else: 38 | refcnt = "-" 39 | used_by = [] 40 | 41 | used = ",".join(used_by) 42 | if used: 43 | used = " " + used 44 | print(f"{name:19} {module_total_size(mod):>8} {refcnt}{used}") 45 | -------------------------------------------------------------------------------- /contrib/mount.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | # Copyright (c) SUSE Linux. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | """A simplified implementation of mount(1) using drgn""" 6 | 7 | from drgn.helpers.linux.fs import for_each_mount, mount_dst, mount_fstype, mount_src 8 | 9 | print("Mount Type Devname Dirname") 10 | for mount in for_each_mount(prog): 11 | maddr = mount.value_() 12 | src = mount_src(mount).decode() 13 | dst = mount_dst(mount).decode() 14 | type_ = mount_fstype(mount).decode() 15 | 16 | print(f"{maddr:<16x} {type_:<12} {src:<12} {dst}") 17 | -------------------------------------------------------------------------------- /contrib/platform_drivers.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """Print registered platform drivers.""" 5 | 6 | from drgn import NULL, container_of 7 | from drgn.helpers.linux.list import list_for_each_entry 8 | 9 | 10 | def bus_to_subsys(bus): 11 | for sp in list_for_each_entry( 12 | "struct subsys_private", 13 | prog["bus_kset"].list.address_of_(), 14 | "subsys.kobj.entry", 15 | ): 16 | if sp.bus == bus: 17 | return sp 18 | return NULL(bus.prog_, "struct subsys_private *") 19 | 20 | 21 | sp = bus_to_subsys(prog["platform_bus_type"].address_of_()) 22 | for priv in list_for_each_entry( 23 | "struct driver_private", sp.drivers_kset.list.address_of_(), "kobj.entry" 24 | ): 25 | driver = priv.driver 26 | print(driver.name.string_().decode()) 27 | platform_driver = container_of(driver, "struct platform_driver", "driver") 28 | print(platform_driver) 29 | -------------------------------------------------------------------------------- /contrib/pp_leak/ls_pp_leaks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | 3 | # Copyright (c) 2025 NVIDIA Corporation & Affiliates 4 | # SPDX-License-Identifier: LGPL-2.1-or-later 5 | 6 | help=""" 7 | Detect leaked page_pool pages by scanning through all the pages. 8 | 9 | Has options for peeking into the page memory and showing the struct page. 10 | """ 11 | 12 | 13 | import argparse 14 | from drgn import FaultError 15 | from drgn.helpers.common.memory import ( 16 | print_annotated_memory 17 | ) 18 | from drgn.helpers.linux.mm import ( 19 | for_each_page, 20 | page_to_virt 21 | ) 22 | from drgn.helpers.linux.net import is_pp_page 23 | 24 | 25 | def get_opts(): 26 | parser = argparse.ArgumentParser(description=help) 27 | parser.add_argument( 28 | "-l", "--peek", default=100, type=int, help="Peek into page given amount of bytes.") 29 | parser.add_argument( 30 | "-s", "--show", default=False, action="store_true", help="Show page struct.") 31 | 32 | args = parser.parse_args() 33 | return args 34 | 35 | 36 | opt = get_opts() 37 | 38 | for page in for_each_page(): 39 | try: 40 | if is_pp_page(page) and page.pp.user.detach_time > 0: 41 | if opt.show: 42 | print(page) 43 | else: 44 | print(f"Leaked page: {hex(page)}") 45 | if opt.peek > 0: 46 | print("Page content: ") 47 | print_annotated_memory(page_to_virt(page), opt.peek) 48 | except FaultError: 49 | continue 50 | 51 | -------------------------------------------------------------------------------- /contrib/vmmap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | # Copyright (c) SUSE Linux. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | """Print memory map of a given task.""" 6 | 7 | import os 8 | import sys 9 | 10 | from drgn.helpers.linux.device import MAJOR, MINOR 11 | from drgn.helpers.linux.fs import d_path 12 | from drgn.helpers.linux.mm import for_each_vma 13 | from drgn.helpers.linux.pid import find_task 14 | 15 | if len(sys.argv) != 2: 16 | sys.exit("Usage: ./vmmap.py PID") 17 | pid = int(sys.argv[1]) 18 | 19 | task = find_task(prog, int(pid)) 20 | if not task: 21 | sys.exit(f"Cannot find task {pid}") 22 | 23 | FLAGS = ((0x1, "r"), (0x2, "w"), (0x4, "x")) 24 | PAGE_SHIFT = prog["PAGE_SHIFT"] 25 | 26 | print("Start End Flgs Offset Dev Inode File path") 27 | 28 | for vma in for_each_vma(task.mm): 29 | flags = "".join([v if f & vma.vm_flags else "-" for f, v in FLAGS]) 30 | flags += "s" if vma.vm_flags & 0x8 else "p" 31 | print(f"{vma.vm_start.value_():0x}-{vma.vm_end.value_():0x} {flags} ", 32 | end="") 33 | 34 | vmfile = vma.vm_file 35 | if vmfile: 36 | inode = vmfile.f_inode.i_ino.value_() 37 | dev = vmfile.f_inode.i_sb.s_dev 38 | major, minor = MAJOR(dev), MINOR(dev) 39 | path = os.fsdecode(d_path(vmfile.f_path)) 40 | pgoff = (vma.vm_pgoff << PAGE_SHIFT).value_() 41 | else: 42 | inode = 0 43 | major, minor = 0, 0 44 | path = "" 45 | pgoff = 0 46 | 47 | print(f"{pgoff:08x} {major:02x}:{minor:02x} {inode:<16} {path}") 48 | -------------------------------------------------------------------------------- /contrib/vmstat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drgn 2 | # Copyright (c) SUSE Linux. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | """Dump /proc/vmstat statistics.""" 6 | 7 | from drgn.helpers.linux.cpumask import for_each_online_cpu 8 | from drgn.helpers.linux.percpu import per_cpu 9 | 10 | 11 | def print_event_line(event, counter): 12 | print(f"{event.name:<36} {counter.value_():>16}") 13 | 14 | 15 | print(f"{'Event':<36} {'Count':>16}") 16 | 17 | # For all of the below, we skip the last enumerator item as it holds the number 18 | # of enumerators. 19 | 20 | # 1) vm_zone_stat statistics are there since v4.8. 21 | if "vm_zone_stat" in prog: 22 | print("VM_ZONE_STAT:") 23 | vm_zone_stat = prog["vm_zone_stat"] 24 | for event in prog.type("enum zone_stat_item").enumerators[:-1]: 25 | print_event_line(event, vm_zone_stat[event.value].counter) 26 | print() 27 | 28 | # 2) vm_node_stat statistics are there since v4.8. 29 | if "vm_node_stat" in prog: 30 | print("VM_NODE_STAT:") 31 | vm_node_stat = prog["vm_node_stat"] 32 | for event in prog.type("enum node_stat_item").enumerators[:-1]: 33 | print_event_line(event, vm_node_stat[event.value].counter) 34 | print() 35 | 36 | # 3) vm_numa_event statistics are there since v5.14. They are only populated if 37 | # CONFIG_NUMA is enabled. 38 | if "node_subsys" in prog and "vm_numa_event" in prog: 39 | print("VM_NUMA_EVENT:") 40 | vm_numa_event = prog["vm_numa_event"] 41 | for event in prog.type("enum numa_stat_item").enumerators[:-1]: 42 | print_event_line(event, vm_numa_event[event.value].counter) 43 | print() 44 | 45 | # 4) vm_event_states statistics (uses per-CPU counters) 46 | print("VM_EVENT_STATES:") 47 | vm_event_states = prog["vm_event_states"] 48 | cpulist = list(for_each_online_cpu(prog)) 49 | for event in prog.type("enum vm_event_item").enumerators[:-1]: 50 | count = sum([per_cpu(vm_event_states, cpu).event[event.value] for cpu in cpulist]) 51 | print_event_line(event, count) 52 | -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | div.sphinxsidebar p.caption { 2 | font-weight: 300; 3 | font-size: 1.4rem; 4 | } 5 | 6 | details { 7 | margin-block-start: 1em; 8 | margin-block-end: 1em; 9 | } 10 | 11 | div.admonition { 12 | padding-bottom: 0; 13 | } 14 | 15 | div.admonition p.admonition-title { 16 | font-size: 17px; 17 | font-weight: bold; 18 | } 19 | 20 | div.tip { 21 | background-color: #DFD; 22 | border-color: #ACA; 23 | } 24 | 25 | div.scroll-y pre { 26 | max-height: 20em; 27 | overflow-y: auto; 28 | } 29 | 30 | div.tutorial pre { 31 | border-left: 5px solid #5A5; 32 | } 33 | 34 | @media screen and (min-width: 875px) { 35 | div.document { 36 | width: 100%; 37 | } 38 | } 39 | @media screen and (min-width: 1095px) { 40 | div.document { 41 | width: 1095px; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/_static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/docs/_static/logo.png -------------------------------------------------------------------------------- /docs/case_studies.rst: -------------------------------------------------------------------------------- 1 | Case Studies 2 | ============ 3 | 4 | These are writeups of real-world problems solved with drgn. 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | 9 | case_studies/dm_crypt_key.rst 10 | case_studies/kyber_stack_trace.rst 11 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import sys 3 | 4 | sys.path.append(os.path.abspath("..")) 5 | sys.path.append(os.path.abspath("exts")) 6 | 7 | master_doc = "index" 8 | 9 | man_pages = [ 10 | ("man/drgn", "drgn", "programmable debugger", "", "1"), 11 | ] 12 | 13 | option_emphasise_placeholders = True 14 | 15 | extensions = [ 16 | "details", 17 | "drgndoc.ext", 18 | "linuxsrc", 19 | "setuptools_config", 20 | "sphinx.ext.extlinks", 21 | "sphinx.ext.graphviz", 22 | "sphinx.ext.intersphinx", 23 | ] 24 | 25 | drgndoc_paths = ["../drgn", "../_drgn.pyi"] 26 | drgndoc_substitutions = [ 27 | (r"^_drgn\b", "drgn"), 28 | ] 29 | drgndoc_submodule_sort = [ 30 | # Sort experimental helpers after everything else. 31 | (r"drgn\.helpers", [(r"experimental", 1)]), 32 | ] 33 | 34 | extlinks = { 35 | "contrib": ( 36 | "https://github.com/osandov/drgn/blob/main/contrib/%s", 37 | "%s", 38 | ), 39 | } 40 | 41 | intersphinx_mapping = { 42 | "python": ("https://docs.python.org/3", None), 43 | } 44 | 45 | manpages_url = "http://man7.org/linux/man-pages/man{section}/{page}.{section}.html" 46 | 47 | html_static_path = ["_static"] 48 | 49 | html_theme = "alabaster" 50 | 51 | html_theme_options = { 52 | "description": "Programmable debugger", 53 | "logo": "logo.png", 54 | "logo_name": True, 55 | "logo_text_align": "center", 56 | "github_user": "osandov", 57 | "github_repo": "drgn", 58 | "github_button": True, 59 | "github_type": "star", 60 | } 61 | 62 | html_favicon = "favicon.ico" 63 | -------------------------------------------------------------------------------- /docs/exts/drgndoc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/docs/exts/drgndoc/__init__.py -------------------------------------------------------------------------------- /docs/exts/drgndoc/util.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from typing import Optional 5 | 6 | 7 | def dot_join(*args: Optional[str]) -> str: 8 | return ".".join([s for s in args if s]) 9 | -------------------------------------------------------------------------------- /docs/exts/drgndoc/visitor.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import ast 5 | from typing import Any, Optional 6 | 7 | 8 | class NodeVisitor: 9 | """ 10 | Node visitor based on ast.NodeVisitor that also passes the parent node and 11 | (right) sibling node. 12 | """ 13 | 14 | def visit(self, node: ast.AST) -> Any: 15 | return self._visit(node, None, None) 16 | 17 | def _visit( 18 | self, node: ast.AST, parent: Optional[ast.AST], sibling: Optional[ast.AST] 19 | ) -> Any: 20 | method = "visit_" + node.__class__.__name__ 21 | visitor = getattr(self, method, None) 22 | if visitor is None: 23 | self.generic_visit(node) 24 | else: 25 | return visitor(node, parent, sibling) 26 | 27 | def generic_visit(self, node: ast.AST) -> None: 28 | for field, value in ast.iter_fields(node): 29 | if isinstance(value, list): 30 | prev = None 31 | for item in value: 32 | if isinstance(item, ast.AST): 33 | if prev: 34 | self._visit(prev, node, item) 35 | prev = item 36 | if prev: 37 | self._visit(prev, node, None) 38 | elif isinstance(value, ast.AST): 39 | self._visit(value, node, None) 40 | -------------------------------------------------------------------------------- /docs/exts/setuptools_config.py: -------------------------------------------------------------------------------- 1 | # Copyright Jason R. Coombs 2 | # SPDX-License-Identifier: MIT 3 | # Based on https://pypi.org/project/jaraco.packaging/. 4 | 5 | import os 6 | import subprocess 7 | import sys 8 | 9 | 10 | def setup(app): 11 | app.add_config_value("package_url", "", "") 12 | app.connect("config-inited", load_config_from_setup) 13 | app.connect("html-page-context", add_package_url) 14 | return {"parallel_read_safe": "True"} 15 | 16 | 17 | def load_config_from_setup(app, config): 18 | """ 19 | Replace values in config from package metadata 20 | """ 21 | # for now, assume project root is one level up 22 | root = os.path.join(app.confdir, "..") 23 | setup_script = os.path.join(root, "setup.py") 24 | fields = ["--name", "--version", "--url", "--author"] 25 | dist_info_cmd = [sys.executable, setup_script] + fields 26 | output = subprocess.check_output(dist_info_cmd, cwd=root, universal_newlines=True) 27 | outputs = output.strip().split("\n") 28 | project, version, url, author = outputs 29 | config.project = project 30 | config.version = config.release = version 31 | config.package_url = url 32 | config.author = config.copyright = author 33 | 34 | 35 | def add_package_url(app, pagename, templatename, context, doctree): 36 | context["package_url"] = app.config.package_url 37 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/docs/favicon.ico -------------------------------------------------------------------------------- /docs/helpers.rst: -------------------------------------------------------------------------------- 1 | .. drgndoc:: drgn.helpers 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | drgn 2 | ==== 3 | 4 | .. include:: ../README.rst 5 | :start-after: start-introduction 6 | :end-before: end-introduction 7 | 8 | In addition to the main Python API, an experimental C library, ``libdrgn``, is 9 | also available. 10 | 11 | See the :doc:`installation` instructions. Then, start with the 12 | :doc:`user_guide`. 13 | 14 | .. include:: ../README.rst 15 | :start-after: start-for-index 16 | :end-before: end-for-index 17 | 18 | Acknowledgements 19 | ---------------- 20 | 21 | drgn is named after `this 22 | `_ because dragons eat 23 | `dwarves `_. 24 | 25 | Table of Contents 26 | ----------------- 27 | 28 | .. toctree:: 29 | :caption: Using drgn 30 | :maxdepth: 3 31 | 32 | installation 33 | user_guide 34 | advanced_usage 35 | getting_debugging_symbols 36 | 37 | .. toctree:: 38 | :caption: Tutorials 39 | :maxdepth: 3 40 | 41 | tutorials 42 | case_studies 43 | 44 | .. toctree:: 45 | :caption: Reference 46 | :maxdepth: 3 47 | 48 | api_reference 49 | helpers 50 | Man Page 51 | support_matrix 52 | release_highlights 53 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | There are several options for installing drgn. 5 | 6 | Dependencies 7 | ------------ 8 | 9 | drgn depends on: 10 | 11 | - `Python `_ 3.6 or newer 12 | - `elfutils `_ 0.165 or newer 13 | 14 | It optionally depends on: 15 | 16 | - `libkdumpfile `_ for `makedumpfile 17 | `_ compressed kernel core dump 18 | format support 19 | - `liblzma `_ for `MiniDebuginfo 20 | `_ 21 | support 22 | 23 | The build requires: 24 | 25 | - `GCC `_ 26 | - `GNU Make `_ 27 | - `pkgconf `_ 28 | - `setuptools `_ 29 | 30 | Running tests requires: 31 | 32 | - `check `_ 0.10.0 or newer 33 | 34 | Building from the Git repository (rather than a release tarball) additionally 35 | requires: 36 | 37 | - `autoconf `_ 38 | - `automake `_ 39 | - `libtool `_ 40 | 41 | .. include:: ../README.rst 42 | :start-after: start-installation 43 | :end-before: end-installation 44 | 45 | .. highlight:: console 46 | 47 | Virtual Environment 48 | ^^^^^^^^^^^^^^^^^^^ 49 | 50 | The above options all install drgn globally. You can also install drgn in a 51 | `virtual environment `_, either 52 | with pip:: 53 | 54 | $ python3 -m venv drgnenv 55 | $ source drgnenv/bin/activate 56 | (drgnenv) $ pip3 install drgn 57 | (drgnenv) $ drgn --help 58 | 59 | Or from source:: 60 | 61 | $ python3 -m venv drgnenv 62 | $ source drgnenv/bin/activate 63 | (drgnenv) $ python3 setup.py install 64 | (drgnenv) $ drgn --help 65 | 66 | Running Locally 67 | --------------- 68 | 69 | If you build drgn from source, you can also run it without installing it:: 70 | 71 | $ python3 setup.py build_ext -i 72 | $ python3 -m drgn --help 73 | -------------------------------------------------------------------------------- /docs/release_highlights.rst: -------------------------------------------------------------------------------- 1 | Release Highlights 2 | ================== 3 | 4 | These are highlights of each release of drgn focusing on a few exciting items 5 | from the full `release notes `_. 6 | 7 | .. toctree:: 8 | 9 | release_highlights/0.0.31.rst 10 | release_highlights/0.0.30.rst 11 | release_highlights/0.0.28.rst 12 | release_highlights/0.0.27.rst 13 | release_highlights/0.0.26.rst 14 | release_highlights/0.0.25.rst 15 | release_highlights/0.0.24.rst 16 | release_highlights/0.0.23.rst 17 | release_highlights/0.0.22.rst 18 | -------------------------------------------------------------------------------- /docs/release_highlights/0.0.30.rst: -------------------------------------------------------------------------------- 1 | 0.0.30 (Released December 18th, 2024) 2 | ===================================== 3 | 4 | These are some of the highlights of drgn 0.0.30. See the `GitHub release 5 | `_ for the full release 6 | notes, including more improvements and bug fixes. 7 | 8 | This release is relatively small, as most development effort has been focused 9 | on the upcoming `module API `_, 10 | which is expected to land in the next release. 11 | 12 | Symbol Index and Kallsyms 13 | ------------------------- 14 | 15 | Stephen Brennan continued his efforts towards `making it possible to debug the 16 | Linux kernel without full DWARF debugging information 17 | `_. The next step in this release 18 | was adding new helpers for looking up symbols from kallsyms: 19 | :func:`~drgn.helpers.linux.kallsyms.load_vmlinux_kallsyms()` and 20 | :func:`~drgn.helpers.linux.kallsyms.load_module_kallsyms()`. These are built on 21 | top of a new, generic API for fast symbol lookups: :class:`drgn.SymbolIndex`. 22 | 23 | New Python 3.13 REPL 24 | -------------------- 25 | 26 | Python 3.13 `added 27 | `_ 28 | a vastly improved REPL with multiline editing, colorized output, interactive 29 | help, and more. drgn now makes use of this REPL when it is available. 30 | 31 | Stack Tracing Through Interrupt Handlers 32 | ---------------------------------------- 33 | 34 | drgn had a longstanding `bug `_ 35 | where stack traces would stop at an interrupt handler frame. This release fixes 36 | that (as long as the kernel is configured to use the ORC unwinder). 37 | 38 | Linux 6.13 Support 39 | ------------------ 40 | 41 | No drgn changes were required to support Linux 6.13 as of rc3. 42 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx==8.1.3 2 | -------------------------------------------------------------------------------- /docs/tutorials.rst: -------------------------------------------------------------------------------- 1 | Tutorials 2 | ========= 3 | 4 | Hands-on tutorials for learning how to use drgn. 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | 9 | tutorials/blk_rq_qos_crash.rst 10 | -------------------------------------------------------------------------------- /drgn/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | drgn entry point 6 | 7 | This module runs the drgn CLI. There is nothing interesting here. 8 | 9 | $ python3 -m drgn --help 10 | """ 11 | 12 | 13 | if __name__ == "__main__": 14 | from drgn.cli import _main 15 | 16 | _main() 17 | -------------------------------------------------------------------------------- /drgn/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Helpers 6 | ------- 7 | 8 | The ``drgn.helpers`` package contains subpackages which provide helpers for 9 | working with particular types of programs. Currently, there are common helpers 10 | and helpers for the Linux kernel. In the future, there may be helpers for, 11 | e.g., glibc and libstdc++. 12 | """ 13 | 14 | 15 | class ValidationError(Exception): 16 | """ 17 | Error raised by a :ref:`validator ` when an inconsistent or 18 | invalid state is detected. 19 | """ 20 | -------------------------------------------------------------------------------- /drgn/helpers/common/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Common 6 | ------ 7 | 8 | The ``drgn.helpers.common`` package provides helpers that can be used with any 9 | program. The helpers are available from the individual modules in which they 10 | are defined and from this top-level package. E.g., the following are both 11 | valid: 12 | 13 | >>> from drgn.helpers.common.memory import identify_address 14 | >>> from drgn.helpers.common import identify_address 15 | 16 | Some of these helpers may have additional program-specific behavior but are 17 | otherwise generic. 18 | """ 19 | 20 | import importlib 21 | import pkgutil 22 | from typing import List 23 | 24 | __all__: List[str] = [] 25 | for _module_info in pkgutil.iter_modules(__path__, prefix=__name__ + "."): 26 | _submodule = importlib.import_module(_module_info.name) 27 | _submodule_all = getattr(_submodule, "__all__", ()) 28 | __all__.extend(_submodule_all) 29 | for _name in _submodule_all: 30 | globals()[_name] = getattr(_submodule, _name) 31 | -------------------------------------------------------------------------------- /drgn/helpers/experimental/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Experimental 6 | ------------ 7 | 8 | The ``drgn.helpers.experimental`` package contains experimental helpers with no 9 | stability guarantees. They may change, move to another package, or be removed. 10 | They are not automatically imported by the CLI. 11 | """ 12 | -------------------------------------------------------------------------------- /drgn/helpers/linux/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Linux Kernel 6 | ------------ 7 | 8 | The ``drgn.helpers.linux`` package contains several modules for working with 9 | data structures and subsystems in the Linux kernel. The helpers are available 10 | from the individual modules in which they are defined and from this top-level 11 | package. E.g., the following are both valid: 12 | 13 | >>> from drgn.helpers.linux.list import list_for_each_entry 14 | >>> from drgn.helpers.linux import list_for_each_entry 15 | 16 | Iterator macros (``for_each_foo``) are a common idiom in the Linux kernel. The 17 | equivalent drgn helpers are implemented as Python :ref:`generators 18 | `. For example, the following code in C: 19 | 20 | .. code-block:: c 21 | 22 | list_for_each(pos, head) 23 | do_something_with(pos); 24 | 25 | Translates to the following code in Python: 26 | 27 | .. code-block:: python3 28 | 29 | for pos in list_for_each(head): 30 | do_something_with(pos) 31 | """ 32 | 33 | import importlib 34 | import pkgutil 35 | from typing import List 36 | 37 | __all__: List[str] = [] 38 | for _module_info in pkgutil.iter_modules(__path__, prefix=__name__ + "."): 39 | _submodule = importlib.import_module(_module_info.name) 40 | _submodule_all = getattr(_submodule, "__all__", ()) 41 | __all__.extend(_submodule_all) 42 | for _name in _submodule_all: 43 | globals()[_name] = getattr(_submodule, _name) 44 | -------------------------------------------------------------------------------- /drgn/helpers/linux/bitmap.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Bitmaps 6 | ------- 7 | 8 | The ``drgn.helpers.linux.bitmap`` module provides helpers for working with 9 | bitmaps from :linux:`include/linux/bitmap.h`. 10 | 11 | The following helpers from :mod:`drgn.helpers.linux.bitops` also apply to 12 | bitmaps: 13 | 14 | * :func:`~drgn.helpers.linux.bitops.for_each_set_bit()` 15 | * :func:`~drgn.helpers.linux.bitops.for_each_clear_bit()` 16 | * :func:`~drgn.helpers.linux.bitops.test_bit()` 17 | """ 18 | 19 | import operator 20 | import sys 21 | 22 | from drgn import IntegerLike, Object, sizeof 23 | 24 | __all__ = ("bitmap_weight",) 25 | 26 | if sys.version_info >= (3, 10): 27 | _bit_count = int.bit_count # novermin 28 | else: 29 | 30 | # Fallback for old Python versions. Surprisingly, this is faster than any 31 | # bit manipulation tricks. 32 | def _bit_count(n: int) -> int: 33 | return bin(n).count("1") 34 | 35 | 36 | def bitmap_weight(bitmap: Object, size: IntegerLike) -> int: 37 | """ 38 | Return the number of set (one) bits in a bitmap 39 | 40 | :param bitmap: ``unsigned long *`` 41 | :param size: Size of *bitmap* in bits. 42 | """ 43 | size = operator.index(size) 44 | word_bits = 8 * sizeof(bitmap.type_.type) 45 | 46 | weight = 0 47 | for i in range(size // word_bits): 48 | weight += _bit_count(bitmap[i].value_()) 49 | 50 | last_word_bits = size % word_bits 51 | if last_word_bits: 52 | weight += _bit_count( 53 | bitmap[size // word_bits].value_() & ((1 << last_word_bits) - 1) 54 | ) 55 | 56 | return weight 57 | -------------------------------------------------------------------------------- /drgn/helpers/linux/bitops.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Bit Operations 6 | -------------- 7 | 8 | The ``drgn.helpers.linux.bitops`` module provides helpers for common bit 9 | operations in the Linux kernel. 10 | """ 11 | 12 | import operator 13 | from typing import Iterator 14 | 15 | from drgn import IntegerLike, Object, sizeof 16 | 17 | __all__ = ( 18 | "for_each_clear_bit", 19 | "for_each_set_bit", 20 | "test_bit", 21 | ) 22 | 23 | 24 | def for_each_set_bit(bitmap: Object, size: IntegerLike) -> Iterator[int]: 25 | """ 26 | Iterate over all set (one) bits in a bitmap. 27 | 28 | :param bitmap: ``unsigned long *`` 29 | :param size: Size of *bitmap* in bits. 30 | """ 31 | size = operator.index(size) 32 | word_bits = 8 * sizeof(bitmap.type_.type) 33 | for i in range((size + word_bits - 1) // word_bits): 34 | word = bitmap[i].value_() 35 | for j in range(min(word_bits, size - word_bits * i)): 36 | if word & (1 << j): 37 | yield (word_bits * i) + j 38 | 39 | 40 | def for_each_clear_bit(bitmap: Object, size: IntegerLike) -> Iterator[int]: 41 | """ 42 | Iterate over all clear (zero) bits in a bitmap. 43 | 44 | :param bitmap: ``unsigned long *`` 45 | :param size: Size of *bitmap* in bits. 46 | """ 47 | size = operator.index(size) 48 | word_bits = 8 * sizeof(bitmap.type_.type) 49 | for i in range((size + word_bits - 1) // word_bits): 50 | word = bitmap[i].value_() 51 | for j in range(min(word_bits, size - word_bits * i)): 52 | if not (word & (1 << j)): 53 | yield (word_bits * i) + j 54 | 55 | 56 | def test_bit(nr: IntegerLike, bitmap: Object) -> bool: 57 | """ 58 | Return whether a bit in a bitmap is set. 59 | 60 | :param nr: Bit number. 61 | :param bitmap: ``unsigned long *`` 62 | """ 63 | nr = operator.index(nr) 64 | word_bits = 8 * sizeof(bitmap.type_.type) 65 | return ((bitmap[nr // word_bits].value_() >> (nr & (word_bits - 1))) & 1) != 0 66 | -------------------------------------------------------------------------------- /drgn/helpers/linux/boot.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Boot 6 | ---- 7 | 8 | The ``drgn.helpers.linux.boot`` module provides helpers for inspecting the 9 | Linux kernel boot configuration. 10 | """ 11 | 12 | from _drgn import _linux_helper_kaslr_offset, _linux_helper_pgtable_l5_enabled 13 | from drgn import Program 14 | from drgn.helpers.common.prog import takes_program_or_default 15 | 16 | __all__ = ( 17 | "kaslr_offset", 18 | "pgtable_l5_enabled", 19 | ) 20 | 21 | 22 | @takes_program_or_default 23 | def kaslr_offset(prog: Program) -> int: 24 | """ 25 | Get the kernel address space layout randomization offset (zero if it is 26 | disabled). 27 | """ 28 | return _linux_helper_kaslr_offset(prog) 29 | 30 | 31 | @takes_program_or_default 32 | def pgtable_l5_enabled(prog: Program) -> bool: 33 | """Return whether 5-level paging is enabled.""" 34 | return _linux_helper_pgtable_l5_enabled(prog) 35 | -------------------------------------------------------------------------------- /drgn/helpers/linux/device.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Devices 6 | ------- 7 | 8 | The ``drgn.helpers.linux.device`` module provides helpers for working with 9 | Linux devices, including the kernel encoding of ``dev_t``. 10 | """ 11 | 12 | import operator 13 | 14 | from drgn import IntegerLike 15 | 16 | __all__ = ( 17 | "MAJOR", 18 | "MINOR", 19 | "MKDEV", 20 | ) 21 | 22 | 23 | # This hasn't changed since at least v2.6. 24 | _MINORBITS = 20 25 | _MINORMASK = (1 << _MINORBITS) - 1 26 | 27 | 28 | def MAJOR(dev: IntegerLike) -> int: 29 | """ 30 | Return the major ID of a kernel ``dev_t``. 31 | 32 | :param dev: ``dev_t`` object or :class:`int`. 33 | """ 34 | return operator.index(dev) >> _MINORBITS 35 | 36 | 37 | def MINOR(dev: IntegerLike) -> int: 38 | """ 39 | Return the minor ID of a kernel ``dev_t``. 40 | 41 | :param dev: ``dev_t`` object or :class:`int`. 42 | """ 43 | return operator.index(dev) & _MINORMASK 44 | 45 | 46 | def MKDEV(major: IntegerLike, minor: IntegerLike) -> int: 47 | """ 48 | Return a kernel ``dev_t`` from the major and minor IDs. 49 | 50 | :param major: Device major ID. 51 | :param minor: Device minor ID. 52 | """ 53 | return (operator.index(major) << _MINORBITS) | operator.index(minor) 54 | -------------------------------------------------------------------------------- /drgn/helpers/linux/kthread.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Kernel Threads 6 | -------------- 7 | 8 | The ``drgn.helpers.linux.kthread`` module provides helpers for working with 9 | Linux kernel threads, a.k.a. kthreads. 10 | """ 11 | 12 | from drgn import Object, cast, container_of 13 | 14 | __all__ = ( 15 | "kthread_data", 16 | "to_kthread", 17 | ) 18 | 19 | 20 | def to_kthread(task: Object) -> Object: 21 | """ 22 | Get the kthread information for a task. 23 | 24 | >>> to_kthread(find_task(3)) 25 | *(struct kthread *)0xffff8ef600191580 = { 26 | ... 27 | .threadfn = (int (*)(void *))kthread_worker_fn+0x0 = 0xffffffffba1e61b0, 28 | .full_name = (char *)0xffff8ef6003d4ac0 = "pool_workqueue_release", 29 | } 30 | 31 | :param task: ``struct task *`` 32 | :return: ``struct kthread *`` 33 | """ 34 | try: 35 | # Since Linux kernel commit e32cf5dfbe22 ("kthread: Generalize 36 | # pf_io_worker so it can point to struct kthread") (in v5.17), the 37 | # struct kthread * is in task->worker_private. 38 | return cast("struct kthread *", task.worker_private) 39 | except AttributeError: 40 | if "free_kthread_struct" in task.prog_: 41 | # Between that and Linux kernel commit 1da5c46fa965 ("kthread: Make 42 | # struct kthread kmalloc'ed") (in v4.10), it is in 43 | # task->set_child_tid. Unfortunately we can only distinguish this 44 | # by looking for another function added in that commit. 45 | return cast("struct kthread *", task.set_child_tid) 46 | else: 47 | # Before that, task->vfork_done points to kthread->exited. 48 | return container_of(task.vfork_done, "struct kthread", "exited") 49 | 50 | 51 | def kthread_data(task: Object) -> Object: 52 | """ 53 | Get the data that was specified when a kthread was created. 54 | 55 | >>> kthread_data(find_task(3)) 56 | (void *)0xffff8ef6001812c0 57 | 58 | :param task: ``struct task *`` 59 | :return: ``void *`` 60 | """ 61 | return to_kthread(task).data.read_() 62 | -------------------------------------------------------------------------------- /drgn/helpers/linux/list_nulls.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Nulls Lists 6 | ----------- 7 | 8 | The ``drgn.helpers.linux.list_nulls`` module provides helpers for working with 9 | the special version of lists (``struct hlist_nulls_head`` and ``struct 10 | hlist_nulls_node``) in :linux:`include/linux/list_nulls.h` where the end of 11 | list is not a ``NULL`` pointer, but a "nulls" marker. 12 | """ 13 | 14 | from typing import Iterator, Union 15 | 16 | from drgn import Object, Type, container_of 17 | 18 | __all__ = ( 19 | "hlist_nulls_empty", 20 | "hlist_nulls_for_each_entry", 21 | "is_a_nulls", 22 | ) 23 | 24 | 25 | def is_a_nulls(pos: Object) -> bool: 26 | """ 27 | Return whether a a pointer is a nulls marker. 28 | 29 | :param pos: ``struct hlist_nulls_node *`` 30 | """ 31 | return bool(pos.value_() & 1) 32 | 33 | 34 | def hlist_nulls_empty(head: Object) -> bool: 35 | """ 36 | Return whether a nulls hash list is empty. 37 | 38 | :param head: ``struct hlist_nulls_head *`` 39 | """ 40 | return is_a_nulls(head.first) 41 | 42 | 43 | def hlist_nulls_for_each_entry( 44 | type: Union[str, Type], head: Object, member: str 45 | ) -> Iterator[Object]: 46 | """ 47 | Iterate over all the entries in a nulls hash list. 48 | 49 | :param type: Entry type. 50 | :param head: ``struct hlist_nulls_head *`` 51 | :param member: Name of list node member in entry type. 52 | :return: Iterator of ``type *`` objects. 53 | """ 54 | type = head.prog_.type(type) 55 | pos = head.first 56 | while not is_a_nulls(pos): 57 | yield container_of(pos, type, member) 58 | pos = pos.next 59 | -------------------------------------------------------------------------------- /drgn/helpers/linux/nodemask.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) ByteDance, Inc. and its affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | NUMA Node Masks 6 | --------------- 7 | 8 | The ``drgn.helpers.linux.nodemask`` module provides helpers for working with 9 | NUMA node masks from :linux:`include/linux/nodemask.h`. 10 | """ 11 | 12 | from typing import Iterator 13 | 14 | from drgn import IntegerLike, Object, Program 15 | from drgn.helpers.common.prog import takes_program_or_default 16 | from drgn.helpers.linux.bitops import for_each_set_bit, test_bit 17 | 18 | __all__ = ( 19 | "for_each_node", 20 | "for_each_node_mask", 21 | "for_each_node_state", 22 | "for_each_online_node", 23 | "node_state", 24 | ) 25 | 26 | 27 | def for_each_node_mask(mask: Object) -> Iterator[int]: 28 | """ 29 | Iterate over all of the NUMA nodes in the given mask. 30 | 31 | :param mask: ``nodemask_t`` 32 | """ 33 | try: 34 | nr_node_ids = mask.prog_["nr_node_ids"].value_() 35 | except KeyError: 36 | nr_node_ids = 1 37 | return for_each_set_bit(mask.bits, nr_node_ids) 38 | 39 | 40 | @takes_program_or_default 41 | def for_each_node_state(prog: Program, state: IntegerLike) -> Iterator[int]: 42 | """ 43 | Iterate over all NUMA nodes in the given state. 44 | 45 | :param state: ``enum node_states`` (e.g., ``N_NORMAL_MEMORY``) 46 | """ 47 | return for_each_node_mask(prog["node_states"][state]) 48 | 49 | 50 | @takes_program_or_default 51 | def for_each_node(prog: Program) -> Iterator[int]: 52 | """Iterate over all possible NUMA nodes.""" 53 | return for_each_node_state(prog["N_POSSIBLE"]) 54 | 55 | 56 | @takes_program_or_default 57 | def for_each_online_node(prog: Program) -> Iterator[int]: 58 | """Iterate over all online NUMA nodes.""" 59 | return for_each_node_state(prog["N_ONLINE"]) 60 | 61 | 62 | def node_state(node: IntegerLike, state: Object) -> bool: 63 | """ 64 | Return whether the given NUMA node has the given state. 65 | 66 | :param node: NUMA node number. 67 | :param state: ``enum node_states`` (e.g., ``N_NORMAL_MEMORY``) 68 | """ 69 | return test_bit(node, state.prog_["node_states"][state].bits) 70 | -------------------------------------------------------------------------------- /drgn/helpers/linux/percpu.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Per-CPU 6 | ------- 7 | 8 | The ``drgn.helpers.linux.percpu`` module provides helpers for working with 9 | per-CPU allocations from :linux:`include/linux/percpu.h` and per-CPU counters 10 | from :linux:`include/linux/percpu_counter.h`. 11 | """ 12 | 13 | from _drgn import _linux_helper_per_cpu_ptr as per_cpu_ptr 14 | from drgn import IntegerLike, Object 15 | from drgn.helpers.linux.cpumask import for_each_online_cpu 16 | 17 | __all__ = ( 18 | "per_cpu", 19 | "per_cpu_ptr", 20 | "percpu_counter_sum", 21 | ) 22 | 23 | 24 | def per_cpu(var: Object, cpu: IntegerLike) -> Object: 25 | """ 26 | Return the per-CPU variable for a given CPU. 27 | 28 | >>> print(repr(prog["runqueues"])) 29 | Object(prog, 'struct rq', address=0x278c0) 30 | >>> per_cpu(prog["runqueues"], 6).curr.comm 31 | (char [16])"python3" 32 | 33 | :param var: Per-CPU variable, i.e., ``type __percpu`` (not a pointer; use 34 | :func:`per_cpu_ptr()` for that). 35 | :param cpu: CPU number. 36 | :return: ``type`` object. 37 | """ 38 | return per_cpu_ptr(var.address_of_(), cpu)[0] 39 | 40 | 41 | def percpu_counter_sum(fbc: Object) -> int: 42 | """ 43 | Return the sum of a per-CPU counter. 44 | 45 | :param fbc: ``struct percpu_counter *`` 46 | """ 47 | ret = fbc.count.value_() 48 | ptr = fbc.counters 49 | for cpu in for_each_online_cpu(fbc.prog_): 50 | ret += per_cpu_ptr(ptr, cpu)[0].value_() 51 | return ret 52 | -------------------------------------------------------------------------------- /drgn/helpers/linux/radixtree.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Radix Trees 6 | ----------- 7 | 8 | The ``drgn.helpers.linux.radixtree`` module provides helpers for working with 9 | radix trees from :linux:`include/linux/radix-tree.h`. 10 | 11 | .. seealso:: 12 | 13 | `XArrays`_, which were introduced in Linux 4.20 as a replacement for radix 14 | trees. 15 | """ 16 | 17 | from typing import Iterator, Tuple 18 | 19 | from drgn import IntegerLike, Object 20 | from drgn.helpers.linux.xarray import xa_for_each, xa_load 21 | 22 | __all__ = ( 23 | "radix_tree_for_each", 24 | "radix_tree_lookup", 25 | ) 26 | 27 | 28 | def radix_tree_lookup(root: Object, index: IntegerLike) -> Object: 29 | """ 30 | Look up the entry at a given index in a radix tree. 31 | 32 | :param root: ``struct radix_tree_root *`` 33 | :param index: Entry index. 34 | :return: ``void *`` found entry, or ``NULL`` if not found. 35 | """ 36 | return xa_load(root, index) 37 | 38 | 39 | def radix_tree_for_each(root: Object) -> Iterator[Tuple[int, Object]]: 40 | """ 41 | Iterate over all of the entries in a radix tree. 42 | 43 | :param root: ``struct radix_tree_root *`` 44 | :return: Iterator of (index, ``void *``) tuples. 45 | """ 46 | return xa_for_each(root) 47 | -------------------------------------------------------------------------------- /drgn/helpers/linux/stackdepot.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Google LLC 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Stack Depot 6 | ----------- 7 | 8 | The ``drgn.helpers.linux.stackdepot`` module provides helpers for working with 9 | the stack trace storage from :linux:`include/linux/stackdepot.h` used by KASAN 10 | and other kernel debugging tools. 11 | """ 12 | 13 | from typing import Optional 14 | 15 | from drgn import Object, StackTrace, cast, reinterpret 16 | 17 | __all__ = ("stack_depot_fetch",) 18 | 19 | 20 | def stack_depot_fetch(handle: Object) -> Optional[StackTrace]: 21 | """ 22 | Returns a stack trace for the given stack handle. 23 | 24 | :param handle: ``depot_stack_handle_t`` 25 | :return: The stack trace, or ``None`` if not available. 26 | """ 27 | prog = handle.prog_ 28 | handle_parts = reinterpret("union handle_parts", handle) 29 | 30 | # Renamed in Linux kernel commit 961c949b012f ("lib/stackdepot: rename slab 31 | # to pool") (in v6.3). 32 | try: 33 | stack_pools = prog["stack_pools"] 34 | except KeyError: 35 | pool = prog["stack_slabs"][handle_parts.slabindex] 36 | else: 37 | # Linux kernel commit 3ee34eabac2a ("lib/stackdepot: fix first entry 38 | # having a 0-handle") (in v6.9-rc1) changed the meaning of pool_index. 39 | # Linux kernel commit a6c1d9cb9a68 ("stackdepot: rename pool_index to 40 | # pool_index_plus_1") (in v6.9-rc3) renamed pool_index to reflect the 41 | # new meaning. This will therefore be wrong for v6.9-rc[1-2] and 42 | # v6.8.[3-4]. 43 | try: 44 | pool_index = handle_parts.pool_index_plus_1 - 1 45 | except AttributeError: 46 | pool_index = handle_parts.pool_index 47 | pool = stack_pools[pool_index] 48 | 49 | if not pool: 50 | return None 51 | 52 | # This has remained the same since the stack depot was introduced in Linux 53 | # kernel commit cd11016e5f52 ("mm, kasan: stackdepot implementation. Enable 54 | # stackdepot for SLAB") (in v4.6), when it was known as STACK_ALLOC_ALIGN. 55 | DEPOT_STACK_ALIGN = 4 56 | 57 | record = cast( 58 | "struct stack_record *", pool + (handle_parts.offset << DEPOT_STACK_ALIGN) 59 | ) 60 | return prog.stack_trace_from_pcs([record.entries[x] for x in range(record.size)]) 61 | -------------------------------------------------------------------------------- /drgn/helpers/linux/tc.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) ByteDance, Inc. and its affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Traffic Control (TC) 6 | -------------------- 7 | 8 | The ``drgn.helpers.linux.tc`` module provides helpers for working with the 9 | Linux kernel Traffic Control (TC) subsystem. 10 | """ 11 | 12 | import operator 13 | 14 | from drgn import NULL, IntegerLike, Object 15 | from drgn.helpers.linux.list import hlist_for_each_entry, list_for_each_entry 16 | 17 | __all__ = ("qdisc_lookup",) 18 | 19 | 20 | def qdisc_lookup(dev: Object, major: IntegerLike) -> Object: 21 | """ 22 | Get a Qdisc from a device and a major handle number. It is worth noting 23 | that conventionally handles are hexadecimal, e.g. ``10:`` in a ``tc`` 24 | command means major handle 0x10. 25 | 26 | :param dev: ``struct net_device *`` 27 | :param major: Qdisc major handle number. 28 | :return: ``struct Qdisc *`` (``NULL`` if not found) 29 | """ 30 | major = operator.index(major) << 16 31 | 32 | roots = [dev.qdisc] 33 | if dev.ingress_queue: 34 | roots.append(dev.ingress_queue.qdisc_sleeping) 35 | 36 | # Since Linux kernel commit 59cc1f61f09c ("net: sched: convert qdisc linked 37 | # list to hashtable") (in v4.7), a device's child Qdiscs are maintained in 38 | # a hashtable in its struct net_device. Before that, they are maintained in 39 | # a linked list in their root Qdisc. 40 | use_hashtable = dev.prog_.type("struct net_device").has_member("qdisc_hash") 41 | 42 | for root in roots: 43 | if root.handle == major: 44 | return root 45 | 46 | if use_hashtable: 47 | for head in root.dev_queue.dev.qdisc_hash: 48 | for qdisc in hlist_for_each_entry( 49 | "struct Qdisc", head.address_of_(), "hash" 50 | ): 51 | if qdisc.handle == major: 52 | return qdisc 53 | else: 54 | for qdisc in list_for_each_entry( 55 | "struct Qdisc", root.list.address_of_(), "list" 56 | ): 57 | if qdisc.handle == major: 58 | return qdisc 59 | 60 | return NULL(dev.prog_, "struct Qdisc *") 61 | -------------------------------------------------------------------------------- /drgn/helpers/linux/tcp.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | TCP 6 | --- 7 | 8 | The ``drgn.helpers.linux.tcp`` module provides helpers for working with the TCP 9 | protocol in the Linux kernel. 10 | """ 11 | 12 | from drgn import Object, cast 13 | 14 | __all__ = ("sk_tcpstate",) 15 | 16 | 17 | def sk_tcpstate(sk: Object) -> Object: 18 | """ 19 | Return the TCP protocol state of a socket. 20 | 21 | :param sk: ``struct sock *`` 22 | :return: TCP state enum value. 23 | """ 24 | return cast(sk.prog_["TCP_ESTABLISHED"].type_, sk.__sk_common.skc_state) 25 | -------------------------------------------------------------------------------- /drgn/helpers/linux/user.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | Users 6 | ----- 7 | 8 | The ``drgn.helpers.linux.user`` module provides helpers for working with users 9 | in the Linux kernel. 10 | """ 11 | 12 | import operator 13 | from typing import Iterator, Union 14 | 15 | from drgn import NULL, IntegerLike, Object, Program 16 | from drgn.helpers.common.prog import takes_program_or_default 17 | from drgn.helpers.linux.list import hlist_for_each_entry 18 | 19 | __all__ = ( 20 | "find_user", 21 | "for_each_user", 22 | ) 23 | 24 | 25 | def _kuid_val(uid: Union[Object, IntegerLike]) -> int: 26 | if isinstance(uid, Object) and uid.type_.type_name() == "kuid_t": 27 | uid = uid.val 28 | return operator.index(uid) 29 | 30 | 31 | @takes_program_or_default 32 | def find_user(prog: Program, uid: Union[Object, IntegerLike]) -> Object: 33 | """ 34 | Return the user structure with the given UID. 35 | 36 | :param uid: ``kuid_t`` object or integer. 37 | :return: ``struct user_struct *`` (``NULL`` if not found) 38 | """ 39 | try: 40 | uidhashentry = prog.cache["uidhashentry"] 41 | except KeyError: 42 | uidhash_table = prog["uidhash_table"] 43 | uidhash_sz = len(uidhash_table) 44 | uidhash_bits = uidhash_sz.bit_length() - 1 45 | uidhash_mask = uidhash_sz - 1 46 | 47 | def uidhashentry(uid: int) -> Object: 48 | hash = ((uid >> uidhash_bits) + uid) & uidhash_mask 49 | return uidhash_table + hash 50 | 51 | prog.cache["uidhashentry"] = uidhashentry 52 | 53 | uid = _kuid_val(uid) 54 | for user in hlist_for_each_entry( 55 | "struct user_struct", uidhashentry(uid), "uidhash_node" 56 | ): 57 | if user.uid.val == uid: 58 | return user 59 | return NULL(prog, "struct user_struct *") 60 | 61 | 62 | @takes_program_or_default 63 | def for_each_user(prog: Program) -> Iterator[Object]: 64 | """ 65 | Iterate over all users in the system. 66 | 67 | :return: Iterator of ``struct user_struct *`` objects. 68 | """ 69 | for hash_entry in prog["uidhash_table"]: 70 | yield from hlist_for_each_entry( 71 | "struct user_struct", hash_entry, "uidhash_node" 72 | ) 73 | -------------------------------------------------------------------------------- /drgn/internal/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """ 5 | drgn internals 6 | 7 | This package contains modules internal to drgn. You should not use them. 8 | """ 9 | -------------------------------------------------------------------------------- /drgn/internal/repl.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Oracle and/or its affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | """Compatibility shim between drgn and the pyrepl/code modules""" 5 | 6 | import os 7 | import sys 8 | from typing import Any, Dict 9 | 10 | __all__ = ("interact", "readline") 11 | 12 | # Python 3.13 introduces a new REPL implemented by the "_pyrepl" internal 13 | # module. It includes features such as colored output and multiline editing. 14 | # Unfortunately, there is no public API exposing these abilities to users, even 15 | # in the "code" module. We'd like to give the best experience possible, so we'll 16 | # detect _pyrepl and try to use it where possible. 17 | try: 18 | # The official Python interpreter honors this environment variable to 19 | # disable the new REPL. We do the same, which also gives users an escape 20 | # hatch if any of the internals we're messing with change. 21 | if os.environ.get("PYTHON_BASIC_REPL"): 22 | raise ModuleNotFoundError() 23 | 24 | # Unfortunately, the typeshed library behind mypy explicitly removed type 25 | # stubs for these modules. This makes sense as they are private APIs, but it 26 | # means we need to disable mypy checks. 27 | from _pyrepl import readline # type: ignore 28 | from _pyrepl.console import InteractiveColoredConsole # type: ignore 29 | from _pyrepl.simple_interact import ( # type: ignore 30 | run_multiline_interactive_console, 31 | ) 32 | 33 | # This _setup() function clobbers the readline completer, but it is 34 | # protected so it only runs once. Call it early so that our overridden 35 | # completer doesn't get clobbered. 36 | readline._setup({}) 37 | 38 | def interact(local: Dict[str, Any], banner: str) -> None: 39 | console = InteractiveColoredConsole(local) 40 | print(banner, file=sys.stderr) 41 | run_multiline_interactive_console(console) 42 | 43 | except (ModuleNotFoundError, ImportError, AttributeError): 44 | import code 45 | import readline 46 | 47 | def interact(local: Dict[str, Any], banner: str) -> None: 48 | code.interact(banner=banner, exitmsg="", local=local) 49 | -------------------------------------------------------------------------------- /drgn/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/drgn/py.typed -------------------------------------------------------------------------------- /libdrgn/.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | *.lo 3 | *.o 4 | .deps/ 5 | .dirstamp 6 | .libs/ 7 | /Makefile 8 | /Makefile.in 9 | /aclocal.m4 10 | /autom4te.cache 11 | /config.log 12 | /config.status 13 | /configure 14 | /configure~ 15 | /html 16 | /libtool 17 | /c_keywords.inc 18 | /linux_kernel_object_find.inc 19 | /python/constants.c 20 | /python/docstrings.c 21 | -------------------------------------------------------------------------------- /libdrgn/accessors.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | // Create external definitions of accessors. 5 | #define DRGN_ACCESSOR_LINKAGE LIBDRGN_PUBLIC extern 6 | #include "util.h" 7 | #include "drgn_internal.h" 8 | -------------------------------------------------------------------------------- /libdrgn/arch_aarch64_defs.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | REGISTERS = [ 5 | *[DrgnRegister(f"x{i}") for i in range(29)], 6 | DrgnRegister(["x29", "fp"]), 7 | DrgnRegister(["x30", "lr"]), 8 | DrgnRegister("sp"), 9 | DrgnRegister("pstate"), 10 | ] 11 | 12 | REGISTER_LAYOUT = [ 13 | DrgnRegisterLayout("ra_sign_state", size=8, dwarf_number=34), 14 | DrgnRegisterLayout("sp", size=8, dwarf_number=31), 15 | # Callee-saved registers. 16 | *[DrgnRegisterLayout(f"x{i}", size=8, dwarf_number=i) for i in range(19, 31)], 17 | # Caller-saved registers. 18 | *[DrgnRegisterLayout(f"x{i}", size=8, dwarf_number=i) for i in range(19)], 19 | # This pc register is only used for interrupted frames. 20 | DrgnRegisterLayout("pc", size=8, dwarf_number=32), 21 | DrgnRegisterLayout("pstate", size=8, dwarf_number=None), 22 | ] 23 | 24 | STACK_POINTER_REGISTER = "sp" 25 | -------------------------------------------------------------------------------- /libdrgn/arch_arm_defs.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | REGISTERS = [ 5 | DrgnRegister(["r0", "a1"]), 6 | DrgnRegister(["r1", "a2"]), 7 | DrgnRegister(["r2", "a3"]), 8 | DrgnRegister(["r3", "a4"]), 9 | DrgnRegister(["r4", "v1"]), 10 | DrgnRegister(["r5", "v2"]), 11 | DrgnRegister(["r6", "v3"]), 12 | DrgnRegister(["r7", "v4"]), 13 | DrgnRegister(["r8", "v5"]), 14 | DrgnRegister(["r9", "v6", "sb"]), 15 | DrgnRegister(["r10", "v7", "sl"]), 16 | DrgnRegister(["r11", "v8", "fp"]), 17 | DrgnRegister(["r12", "ip"]), 18 | DrgnRegister(["r13", "sp"]), 19 | DrgnRegister(["r14", "lr"]), 20 | DrgnRegister(["r15", "pc"]), 21 | ] 22 | 23 | REGISTER_LAYOUT = [ 24 | DrgnRegisterLayout("r13", size=4, dwarf_number=13), 25 | DrgnRegisterLayout("r14", size=4, dwarf_number=14), 26 | # Callee-saved registers. 27 | *[DrgnRegisterLayout(f"r{i}", size=4, dwarf_number=i) for i in range(4, 12)], 28 | # Caller-saved registers. 29 | *[DrgnRegisterLayout(f"r{i}", size=4, dwarf_number=i) for i in range(4)], 30 | DrgnRegisterLayout("r12", size=4, dwarf_number=12), 31 | DrgnRegisterLayout("r15", size=4, dwarf_number=15), 32 | DrgnRegisterLayout("cpsr", size=4, dwarf_number=None), 33 | ] 34 | 35 | STACK_POINTER_REGISTER = "r13" 36 | -------------------------------------------------------------------------------- /libdrgn/arch_i386.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include 5 | 6 | #include "platform.h" // IWYU pragma: associated 7 | 8 | static struct drgn_error * 9 | apply_elf_reloc_i386(const struct drgn_relocating_section *relocating, 10 | uint64_t r_offset, uint32_t r_type, 11 | const int64_t *r_addend, uint64_t sym_value) 12 | { 13 | switch (r_type) { 14 | case R_386_NONE: 15 | return NULL; 16 | case R_386_32: 17 | return drgn_reloc_add32(relocating, r_offset, r_addend, 18 | sym_value); 19 | case R_386_PC32: 20 | return drgn_reloc_add32(relocating, r_offset, r_addend, 21 | sym_value 22 | - (relocating->addr + r_offset)); 23 | default: 24 | return DRGN_UNKNOWN_RELOCATION_TYPE(r_type); 25 | } 26 | } 27 | 28 | const struct drgn_architecture_info arch_info_i386 = { 29 | .name = "i386", 30 | .arch = DRGN_ARCH_I386, 31 | .default_flags = DRGN_PLATFORM_IS_LITTLE_ENDIAN, 32 | .scalar_alignment = { 1, 2, 4, 4, 16 }, 33 | .register_by_name = drgn_register_by_name_unknown, 34 | .apply_elf_reloc = apply_elf_reloc_i386, 35 | }; 36 | -------------------------------------------------------------------------------- /libdrgn/arch_ppc64_defs.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | REGISTERS = [ 5 | *[DrgnRegister(f"r{i}") for i in range(32)], 6 | DrgnRegister("lr"), 7 | *[DrgnRegister(f"cr{i}") for i in range(8)], 8 | ] 9 | 10 | # There are two conflicting definitions of DWARF register numbers after 63. The 11 | # original definition appears to be "64-bit PowerPC ELF Application Binary 12 | # Interface Supplement" [1]. The GNU toolchain instead uses its own that was 13 | # later codified in "Power Architecture 64-Bit ELF V2 ABI Specification" [2]. 14 | # We use the latter. 15 | # 16 | # 1: https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html 17 | # 2: https://openpowerfoundation.org/specifications/64bitelfabi/ 18 | REGISTER_LAYOUT = [ 19 | DrgnRegisterLayout("lr", size=8, dwarf_number=65), 20 | *[DrgnRegisterLayout(f"r{i}", size=8, dwarf_number=i) for i in range(32)], 21 | *[DrgnRegisterLayout(f"cr{i}", size=8, dwarf_number=68 + i) for i in range(8)], 22 | ] 23 | 24 | STACK_POINTER_REGISTER = "r1" 25 | -------------------------------------------------------------------------------- /libdrgn/arch_s390x_defs.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | REGISTERS = [ 5 | *[DrgnRegister(f"r{i}") for i in range(16)], 6 | *[DrgnRegister(f"a{i}") for i in range(16)], 7 | DrgnRegister("pswm"), 8 | DrgnRegister("pswa"), 9 | ] 10 | 11 | REGISTER_LAYOUT = [ 12 | # Callee-saved registers and return address (r14). 13 | *[DrgnRegisterLayout(f"r{i}", size=8, dwarf_number=i) for i in range(6, 16)], 14 | # Caller-saved registers. 15 | *[DrgnRegisterLayout(f"r{i}", size=8, dwarf_number=i) for i in range(6)], 16 | # These are typically only used for interrupted frames. 17 | DrgnRegisterLayout("pswm", size=8, dwarf_number=64), 18 | DrgnRegisterLayout("pswa", size=8, dwarf_number=65), 19 | # Access control registers (ACRs) are only used in userspace, 20 | # and not present in struct pt_regs. 21 | *[DrgnRegisterLayout(f"a{i}", size=4, dwarf_number=48 + i) for i in range(16)], 22 | ] 23 | 24 | STACK_POINTER_REGISTER = "r15" 25 | -------------------------------------------------------------------------------- /libdrgn/array.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * Helpers for C arrays. 8 | */ 9 | 10 | #ifndef DRGN_ARRAY_H 11 | #define DRGN_ARRAY_H 12 | 13 | #include "pp.h" 14 | #include "util.h" 15 | 16 | /** @cond */ 17 | #define array_for_each_impl(var, arr, unique_end) \ 18 | for (typeof((arr)[0]) *var = (arr), \ 19 | *unique_end = var + array_size(arr); \ 20 | var < unique_end; var++) 21 | /** @endcond */ 22 | 23 | /** 24 | * Return the number of elements in an array. 25 | * 26 | * @hideinitializer 27 | */ 28 | #define array_size(arr) \ 29 | static_assert_expression(is_array(arr), \ 30 | "not an array", \ 31 | sizeof(arr) / sizeof((arr)[0])) 32 | 33 | /** 34 | * Iterate over every element in an array. 35 | * 36 | * The element is declared as `element_type *var` in the scope of the loop. 37 | * 38 | * @hideinitializer 39 | */ 40 | #define array_for_each(var, arr) \ 41 | array_for_each_impl(var, arr, PP_UNIQUE(end)) 42 | 43 | #endif /* DRGN_ARRAY_H */ 44 | -------------------------------------------------------------------------------- /libdrgn/binary_buffer.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "binary_buffer.h" 9 | #include "drgn_internal.h" 10 | 11 | static struct drgn_error *binary_buffer_error_vat(struct binary_buffer *bb, 12 | const char *pos, 13 | const char *format, 14 | va_list ap) 15 | { 16 | char *message; 17 | int ret = vasprintf(&message, format, ap); 18 | if (ret == -1) 19 | return &drgn_enomem; 20 | struct drgn_error *err = bb->error_fn(bb, pos, message); 21 | free(message); 22 | return err; 23 | } 24 | 25 | struct drgn_error *binary_buffer_error(struct binary_buffer *bb, 26 | const char *format, ...) 27 | { 28 | va_list ap; 29 | va_start(ap, format); 30 | struct drgn_error *err = binary_buffer_error_vat(bb, bb->prev, format, 31 | ap); 32 | va_end(ap); 33 | return err; 34 | } 35 | 36 | struct drgn_error *binary_buffer_error_at(struct binary_buffer *bb, 37 | const char *pos, const char *format, 38 | ...) 39 | { 40 | va_list ap; 41 | va_start(ap, format); 42 | struct drgn_error *err = binary_buffer_error_vat(bb, pos, format, ap); 43 | va_end(ap); 44 | return err; 45 | } 46 | -------------------------------------------------------------------------------- /libdrgn/build-aux/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !/.gitignore 3 | !/checkmk 4 | !/codegen_utils.py 5 | !/gen_arch_inc_strswitch.py 6 | !/gen_c_keywords_inc_strswitch.py 7 | !/gen_constants.py 8 | !/gen_elf_sections.py 9 | !/gen_strswitch.py 10 | -------------------------------------------------------------------------------- /libdrgn/build-aux/gen_c_keywords_inc_strswitch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | C_KEYWORDS = ( 6 | "_Atomic", 7 | "_Bool", 8 | "char", 9 | "class", 10 | "const", 11 | "double", 12 | "enum", 13 | "float", 14 | "int", 15 | "long", 16 | "restrict", 17 | "short", 18 | "signed", 19 | "struct", 20 | "union", 21 | "unsigned", 22 | "void", 23 | "volatile", 24 | ) 25 | 26 | 27 | def main() -> None: 28 | keywords = [ 29 | ("C_TOKEN_" + keyword.replace("_", "").upper(), keyword) 30 | for keyword in C_KEYWORDS 31 | ] 32 | print("/* Generated by libdrgn/build-aux/gen_c_keywords_inc_strswitch.py. */") 33 | print("static const char * const keyword_spelling[] = {") 34 | for token_kind, keyword in keywords: 35 | print(f'\t[{token_kind}] = "{keyword}",') 36 | print("};") 37 | print() 38 | print("static int identifier_token_kind(const char *s, size_t len, bool cpp)") 39 | print("{") 40 | print("\t@memswitch (s, len)@") 41 | for token_kind, keyword in keywords: 42 | print(f'\t@case "{keyword}"@') 43 | if keyword == "class": 44 | print(f"\t\treturn cpp ? {token_kind} : C_TOKEN_IDENTIFIER;") 45 | else: 46 | print(f"\t\treturn {token_kind};") 47 | print("\t@default@") 48 | print("\t\treturn C_TOKEN_IDENTIFIER;") 49 | print("\t@endswitch@") 50 | print("}") 51 | 52 | 53 | if __name__ == "__main__": 54 | main() 55 | -------------------------------------------------------------------------------- /libdrgn/c_lexer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #ifndef DRGN_C_LEXER_H 5 | #define DRGN_C_LEXER_H 6 | 7 | #include 8 | 9 | #include "lexer.h" 10 | 11 | // These definitions are only exposed for testing purposes. 12 | 13 | // This obviously incomplete since we only handle the tokens we care about. 14 | enum { 15 | C_TOKEN_EOF = -1, 16 | MIN_KEYWORD_TOKEN, 17 | MIN_SPECIFIER_TOKEN = MIN_KEYWORD_TOKEN, 18 | C_TOKEN_VOID = MIN_SPECIFIER_TOKEN, 19 | C_TOKEN_CHAR, 20 | C_TOKEN_SHORT, 21 | C_TOKEN_INT, 22 | C_TOKEN_LONG, 23 | C_TOKEN_SIGNED, 24 | C_TOKEN_UNSIGNED, 25 | C_TOKEN_BOOL, 26 | C_TOKEN_FLOAT, 27 | C_TOKEN_DOUBLE, 28 | MAX_SPECIFIER_TOKEN = C_TOKEN_DOUBLE, 29 | MIN_QUALIFIER_TOKEN, 30 | C_TOKEN_CONST = MIN_QUALIFIER_TOKEN, 31 | C_TOKEN_RESTRICT, 32 | C_TOKEN_VOLATILE, 33 | C_TOKEN_ATOMIC, 34 | MAX_QUALIFIER_TOKEN = C_TOKEN_ATOMIC, 35 | C_TOKEN_STRUCT, 36 | C_TOKEN_UNION, 37 | C_TOKEN_CLASS, 38 | C_TOKEN_ENUM, 39 | MAX_KEYWORD_TOKEN = C_TOKEN_ENUM, 40 | C_TOKEN_LPAREN, 41 | C_TOKEN_RPAREN, 42 | C_TOKEN_LBRACKET, 43 | C_TOKEN_RBRACKET, 44 | C_TOKEN_ASTERISK, 45 | C_TOKEN_DOT, 46 | C_TOKEN_NUMBER, 47 | C_TOKEN_IDENTIFIER, 48 | C_TOKEN_TEMPLATE_ARGUMENTS, 49 | C_TOKEN_COLON, 50 | }; 51 | 52 | struct drgn_c_family_lexer { 53 | struct drgn_lexer lexer; 54 | bool cpp; 55 | }; 56 | 57 | struct drgn_error *drgn_c_family_lexer_func(struct drgn_lexer *lexer, 58 | struct drgn_token *token); 59 | 60 | #endif /* DRGN_C_LEXER_H */ 61 | -------------------------------------------------------------------------------- /libdrgn/cleanup.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * Cleanup functions. 8 | */ 9 | 10 | #ifndef DRGN_CLEANUP_H 11 | #define DRGN_CLEANUP_H 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define _cleanup_(x) __attribute__((__cleanup__(x))) 20 | 21 | /** Call @c free() when the variable goes out of scope. */ 22 | #define _cleanup_free_ _cleanup_(freep) 23 | static inline void freep(void *p) 24 | { 25 | free(*(void **)p); 26 | } 27 | 28 | /** Call @c fclose() when the variable goes out of scope. */ 29 | #define _cleanup_fclose_ _cleanup_(fclosep) 30 | static inline void fclosep(FILE **fp) 31 | { 32 | if (*fp) 33 | fclose(*fp); 34 | } 35 | 36 | /** Call @c close() when the variable goes out of scope. */ 37 | #define _cleanup_close_ _cleanup_(closep) 38 | static inline void closep(int *fd) 39 | { 40 | if (*fd >= 0) 41 | close(*fd); 42 | } 43 | 44 | /** Call @c closedir() when the variable goes out of scope. */ 45 | #define _cleanup_closedir_ _cleanup_(closedirp) 46 | static inline void closedirp(DIR **dirp) 47 | { 48 | if (*dirp) 49 | closedir(*dirp); 50 | } 51 | 52 | /** 53 | * Get the value of a pointer variable and reset it to @c NULL. 54 | * 55 | * This can be used to avoid freeing a variable declared with @ref 56 | * _cleanup_free_ or another scope guard that is a no-op for @c NULL. 57 | */ 58 | #define no_cleanup_ptr(p) ({ __auto_type __ptr = (p); (p) = NULL; __ptr; }) 59 | 60 | /** 61 | * Return a pointer declared with @ref _cleanup_free_ without freeing it. 62 | * 63 | * This can also be used for other scope guards that are a no-op for @c NULL. 64 | */ 65 | #define return_ptr(p) return no_cleanup_ptr(p) 66 | 67 | #endif /* DRGN_CLEANUP_H */ 68 | -------------------------------------------------------------------------------- /libdrgn/crc32.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * CRC-32 checksums. 8 | * 9 | * See @ref CRC32. 10 | */ 11 | 12 | #ifndef DRGN_CRC32_H 13 | #define DRGN_CRC32_H 14 | 15 | #include 16 | #include 17 | 18 | /** 19 | * @ingroup Internals 20 | * 21 | * @defgroup CRC32 CRC-32 22 | * 23 | * CRC-32 checksums. 24 | * 25 | * @{ 26 | */ 27 | 28 | /** 29 | * Update a CRC-32 checksum with additional data. 30 | * 31 | * This uses the IEEE CRC-32 polynomial (x32 + 32 | * x26 + x23 + x22 + 33 | * x16 + x12 + x11 + 34 | * x10 + x8 + x7 + 35 | * x5 + x4 + x2 + 36 | * x + 1). 37 | * 38 | * @param[in] crc Checksum to update. For the first call, this is the initial 39 | * checksum value (often `0xffffffff`). 40 | * @param[in] buf Data to checksum. 41 | * @param[in] len Size of @p buf in bytes. 42 | * @return Updated checksum. This is not bitwise negated as is often required 43 | * for the final result. 44 | */ 45 | uint32_t crc32_update(uint32_t crc, const void *buf, size_t len); 46 | 47 | /** @} */ 48 | 49 | #endif /* DRGN_CRC32_H */ 50 | -------------------------------------------------------------------------------- /libdrgn/debug_info_options.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #ifndef DRGN_DEBUG_INFO_OPTIONS_H 5 | #define DRGN_DEBUG_INFO_OPTIONS_H 6 | 7 | #include "drgn_internal.h" 8 | 9 | // X macro expanding to all debug info options. 10 | #define DRGN_DEBUG_INFO_OPTIONS \ 11 | LIST_OPTION(directories) \ 12 | BOOL_OPTION(try_module_name, true) \ 13 | BOOL_OPTION(try_build_id, true) \ 14 | LIST_OPTION(debug_link_directories) \ 15 | BOOL_OPTION(try_debug_link, true) \ 16 | BOOL_OPTION(try_procfs, true) \ 17 | BOOL_OPTION(try_embedded_vdso, true) \ 18 | BOOL_OPTION(try_reuse, true) \ 19 | BOOL_OPTION(try_supplementary, true) \ 20 | LIST_OPTION(kernel_directories) \ 21 | ENUM_OPTION(try_kmod, drgn_kmod_search_method, \ 22 | DRGN_KMOD_SEARCH_DEPMOD_OR_WALK) 23 | 24 | struct drgn_debug_info_options { 25 | #define LIST_OPTION(name) const char * const *name; 26 | #define BOOL_OPTION(name, default_value) bool name; 27 | #define ENUM_OPTION(name, type, default_value) enum type name; 28 | DRGN_DEBUG_INFO_OPTIONS 29 | #undef ENUM_OPTION 30 | #undef BOOL_OPTION 31 | #undef LIST_OPTION 32 | }; 33 | 34 | void drgn_debug_info_options_init(struct drgn_debug_info_options *options); 35 | void drgn_debug_info_options_deinit(struct drgn_debug_info_options *options); 36 | 37 | char *drgn_format_debug_info_options(struct drgn_debug_info_options *options); 38 | 39 | #endif /* DRGN_DEBUG_INFO_OPTIONS_H */ 40 | -------------------------------------------------------------------------------- /libdrgn/dwarf_constants.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | // Generated by scripts/gen_dwarf_constants.py. 4 | 5 | #include 6 | 7 | #include "dwarf_constants.h" 8 | 9 | #define X(name, _) if (value == name) return #name; 10 | 11 | const char *dw_op_str(int value, char buf[static DW_OP_STR_BUF_LEN]) 12 | { 13 | DW_OP_DEFINITIONS 14 | snprintf(buf, DW_OP_STR_BUF_LEN, DW_OP_STR_UNKNOWN_FORMAT, value); 15 | return buf; 16 | } 17 | 18 | const char *dw_tag_str(int value, char buf[static DW_TAG_STR_BUF_LEN]) 19 | { 20 | DW_TAG_DEFINITIONS 21 | snprintf(buf, DW_TAG_STR_BUF_LEN, DW_TAG_STR_UNKNOWN_FORMAT, value); 22 | return buf; 23 | } 24 | 25 | #undef X 26 | -------------------------------------------------------------------------------- /libdrgn/elf_symtab.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * ELF symbol tables. 8 | * 9 | * See @ref ElfSymtab. 10 | */ 11 | 12 | #ifndef DRGN_ELF_SYMBOL_H 13 | #define DRGN_ELF_SYMBOL_H 14 | 15 | #include "drgn_internal.h" 16 | 17 | struct drgn_elf_file; 18 | 19 | /** 20 | * @ingroup Internals 21 | * 22 | * @defgroup ElfSymtab ELF symbol tables 23 | * 24 | * ELF symbol table lookups. 25 | * 26 | * @{ 27 | */ 28 | 29 | /** Symbol table from an ELF file. */ 30 | struct drgn_elf_symbol_table { 31 | /** File containing symbol table. @c NULL if not found yet. */ 32 | struct drgn_elf_file *file; 33 | /** Bias to apply to addresses from the file. */ 34 | uint64_t bias; 35 | /** Symbol table section data. */ 36 | const char *data; 37 | /** Number of symbols in table. */ 38 | size_t num_symbols; 39 | /** Number of local symbols in table. */ 40 | size_t num_local_symbols; 41 | /** String table section used by symbol table. */ 42 | Elf_Data *strtab; 43 | /** Optional `SHT_SYMTAB_SHNDX` section used by symbol table. */ 44 | Elf_Data *shndx; 45 | }; 46 | 47 | /** Find matching ELF symbols in a specific module. */ 48 | struct drgn_error * 49 | drgn_module_elf_symbols_search(struct drgn_module *module, const char *name, 50 | uint64_t addr, enum drgn_find_symbol_flags flags, 51 | struct drgn_symbol_result_builder *builder); 52 | 53 | /** @} */ 54 | 55 | #endif /* DRGN_ELF_SYMBOL_H */ 56 | -------------------------------------------------------------------------------- /libdrgn/generics.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * Helpers for generic programming. 8 | */ 9 | 10 | #ifndef DRGN_GENERICS_H 11 | #define DRGN_GENERICS_H 12 | 13 | /** 14 | * Choose a type based on a condition. 15 | * 16 | * @param[in] condition Controlling integer constant expression. 17 | * @param[in] if_true Type if @p condition is non-zero. 18 | * @param[in] if_false Type if @p condition is zero. 19 | */ 20 | #define type_if(condition, if_true, if_false) \ 21 | __typeof__( \ 22 | /* + 1 avoids a non-standard zero-length array. */ \ 23 | *_Generic((int (*)[!(condition) + 1])0, \ 24 | int (*)[1]: (__typeof__(if_true) *)0, \ 25 | int (*)[2]: (__typeof__(if_false) *)0) \ 26 | ) 27 | 28 | /** 29 | * Define a typedef based on a condition. 30 | * 31 | * @param[in] name Name of type. 32 | * @param[in] condition Controlling integer constant expression. 33 | * @param[in] if_true Type if @p condition is non-zero. 34 | * @param[in] if_false Type if @p condition is zero. 35 | */ 36 | #define typedef_if(name, condition, if_true, if_false) \ 37 | typedef type_if(condition, if_true, if_false) name 38 | 39 | #endif /* DRGN_GENERICS_H */ 40 | -------------------------------------------------------------------------------- /libdrgn/hash_table.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include "hash_table.h" 5 | 6 | const uint8_t hash_table_empty_chunk_header[16] __attribute__((__aligned__(16))); 7 | -------------------------------------------------------------------------------- /libdrgn/hexlify.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include 5 | #include 6 | 7 | #include "hexlify.h" 8 | 9 | void hexlify(const void *in, size_t in_len, char *out) 10 | { 11 | static const char nibble_to_hex_digit[] = "0123456789abcdef"; 12 | for (size_t i = 0; i < in_len; i++) { 13 | uint8_t byte = ((uint8_t *)in)[i]; 14 | out[2 * i] = nibble_to_hex_digit[byte >> 4]; 15 | out[2 * i + 1] = nibble_to_hex_digit[byte & 0xf]; 16 | } 17 | } 18 | 19 | char *ahexlify(const void *in, size_t in_len) 20 | { 21 | size_t out_size; 22 | if (__builtin_mul_overflow(in_len, 2U, &out_size) || 23 | __builtin_add_overflow(out_size, 1U, &out_size)) 24 | return NULL; 25 | char *out = malloc(out_size); 26 | if (!out) 27 | return NULL; 28 | hexlify(in, in_len, out); 29 | out[out_size - 1] = '\0'; 30 | return out; 31 | } 32 | 33 | static inline bool hex_digit_to_nibble(char c, uint8_t *ret) 34 | { 35 | if ('0' <= c && c <= '9') 36 | *ret = c - '0'; 37 | else if ('a' <= c && c <= 'f') 38 | *ret = c - 'a' + 10; 39 | else if ('A' <= c && c <= 'F') 40 | *ret = c - 'A' + 10; 41 | else 42 | return false; 43 | return true; 44 | } 45 | 46 | bool unhexlify(const char *in, size_t in_len, void *out) 47 | { 48 | if (in_len % 2) 49 | return false; 50 | for (size_t i = 0; i < in_len; i += 2) { 51 | uint8_t lo, hi; 52 | if (!hex_digit_to_nibble(in[i], &hi) || 53 | !hex_digit_to_nibble(in[i + 1], &lo)) 54 | return false; 55 | ((uint8_t *)out)[i / 2] = (hi << 4) | lo; 56 | } 57 | return true; 58 | } 59 | -------------------------------------------------------------------------------- /libdrgn/hexlify.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * Hexadecimal encoding/decoding. 8 | * 9 | * See @ref Hexlify. 10 | */ 11 | 12 | #ifndef DRGN_HEXLIFY_H 13 | #define DRGN_HEXLIFY_H 14 | 15 | #include 16 | #include 17 | 18 | /** 19 | * @ingroup Internals 20 | * 21 | * @defgroup Hexlify Hexlify 22 | * 23 | * Hexadecimal encoding/decoding. 24 | * 25 | * @{ 26 | */ 27 | 28 | /** 29 | * Encode binary data to a hexadecimal string. 30 | * 31 | * The output string is an even number of lowercase hexadecimal characters with 32 | * no separators. It is not null-terminated. 33 | * 34 | * @param[in] in Input binary data. 35 | * @param[in] in_len Size of @p in in bytes. 36 | * @param[out] out Output hexadecimal string of size `2 * in_len` characters. 37 | * Not null-terminated. 38 | */ 39 | void hexlify(const void *in, size_t in_len, char *out); 40 | 41 | /** 42 | * Allocate and encode binary data to a hexadecimal string. 43 | * 44 | * This is like @ref hexlify(), but it allocates the output string, including a 45 | * terminating null byte. 46 | * 47 | * @param[in] in Input binary data. 48 | * @param[in] in_len Size of @p in in bytes. 49 | * @return Output hexadecimal string, or `NULL` on failure to allocate memory. 50 | * Unlike @ref hexlify(), this *is* null-terminated. On success, it must be 51 | * freed with `free()`. 52 | */ 53 | char *ahexlify(const void *in, size_t in_len); 54 | 55 | /** 56 | * Decode hexadecimal string to binary data. 57 | * 58 | * The input string must be an even number of hexadecimal characters (either 59 | * lowercase or uppercase) with no separators. 60 | * 61 | * @param[in] in Input hexadecimal string. Does not need to be null-terminated. 62 | * @param[in] in_len Number of characters in @p in. 63 | * @param[out] out Returned binary data of size `in_len / 2` bytes. 64 | * @return `true` if data was successfully decoded, `false` if not (either 65 | * because @p in_len was odd or @p in contained non-hexadecimal characters). 66 | */ 67 | bool unhexlify(const char *in, size_t in_len, void *out); 68 | 69 | /** @} */ 70 | 71 | #endif /* DRGN_HEXLIFY_H */ 72 | -------------------------------------------------------------------------------- /libdrgn/io.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "io.h" 9 | 10 | ssize_t read_all(int fd, void *buf, size_t count) 11 | { 12 | if (count > SSIZE_MAX) { 13 | errno = EINVAL; 14 | return -1; 15 | } 16 | size_t n = 0; 17 | while (n < count) { 18 | ssize_t r = read(fd, (char *)buf + n, count - n); 19 | if (r < 0) { 20 | if (errno == EINTR) 21 | continue; 22 | return r; 23 | } else if (r == 0) { 24 | break; 25 | } 26 | n += r; 27 | } 28 | return n; 29 | } 30 | 31 | ssize_t pread_all(int fd, void *buf, size_t count, off_t offset) 32 | { 33 | if (count > SSIZE_MAX) { 34 | errno = EINVAL; 35 | return -1; 36 | } 37 | size_t n = 0; 38 | while (n < count) { 39 | ssize_t r = pread(fd, (char *)buf + n, count - n, offset + n); 40 | if (r < 0) { 41 | if (errno == EINTR) 42 | continue; 43 | return r; 44 | } else if (r == 0) { 45 | break; 46 | } 47 | n += r; 48 | } 49 | return n; 50 | } 51 | -------------------------------------------------------------------------------- /libdrgn/io.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * Input/output helpers. 8 | */ 9 | 10 | #ifndef DRGN_IO_H 11 | #define DRGN_IO_H 12 | 13 | #include 14 | #include 15 | 16 | /** 17 | * Wrapper around \manpage{read,2} that never returns less bytes than requested unless it 18 | * hits end-of-file. 19 | */ 20 | ssize_t read_all(int fd, void *buf, size_t count); 21 | 22 | /** 23 | * Wrapper around \manpage{pread,2} that never returns less bytes than requested unless 24 | * it hits end-of-file. 25 | */ 26 | ssize_t pread_all(int fd, void *buf, size_t count, off_t offset); 27 | 28 | #endif /* DRGN_IO_H */ 29 | -------------------------------------------------------------------------------- /libdrgn/kallsyms.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Oracle and/or its affiliates 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * Kallsyms data handling 8 | * 9 | * See @ref Kallsyms 10 | */ 11 | 12 | #ifndef DRGN_KALLSYMS_H 13 | #define DRGN_KALLSYMS_H 14 | 15 | #include 16 | #include 17 | 18 | #include "hash_table.h" 19 | #include "symbol.h" 20 | 21 | struct kallsyms_locations { 22 | uint64_t kallsyms_names; 23 | uint64_t kallsyms_token_table; 24 | uint64_t kallsyms_token_index; 25 | uint64_t kallsyms_num_syms; 26 | uint64_t kallsyms_offsets; 27 | uint64_t kallsyms_relative_base; 28 | uint64_t kallsyms_addresses; 29 | uint64_t _stext; 30 | }; 31 | 32 | /** 33 | * Initialize a symbol index containing symbols from /proc/kallsyms 34 | */ 35 | struct drgn_error *drgn_load_proc_kallsyms(const char *filename, bool modules, 36 | struct drgn_symbol_index *ret); 37 | 38 | /** 39 | * Initialize a symbol index containing symbols from built-in kallsyms tables 40 | */ 41 | struct drgn_error * 42 | drgn_load_builtin_kallsyms(struct drgn_program *prog, 43 | struct kallsyms_locations *loc, 44 | struct drgn_symbol_index *ret); 45 | 46 | #endif // DRGN_KALLSYMS_H 47 | -------------------------------------------------------------------------------- /libdrgn/language.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include 5 | 6 | #include "array.h" 7 | #include "language.h" 8 | #include "util.h" 9 | 10 | const struct drgn_language * const drgn_languages[] = { 11 | [DRGN_LANGUAGE_C] = &drgn_language_c, 12 | [DRGN_LANGUAGE_CPP] = &drgn_language_cpp, 13 | }; 14 | static_assert(array_size(drgn_languages) == DRGN_NUM_LANGUAGES, 15 | "missing language in drgn_languages"); 16 | 17 | LIBDRGN_PUBLIC const char *drgn_language_name(const struct drgn_language *lang) 18 | { 19 | return lang->name; 20 | } 21 | -------------------------------------------------------------------------------- /libdrgn/lazy_object.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include 5 | 6 | #include "lazy_object.h" 7 | 8 | static_assert(offsetof(union drgn_lazy_object, obj.type) == 9 | offsetof(union drgn_lazy_object, thunk.dummy_type), 10 | "drgn_lazy_object layout is invalid"); 11 | 12 | struct drgn_error *drgn_lazy_object_evaluate(union drgn_lazy_object *lazy_obj) 13 | { 14 | struct drgn_error *err; 15 | if (!drgn_lazy_object_is_evaluated(lazy_obj)) { 16 | struct drgn_program *prog = lazy_obj->thunk.prog; 17 | drgn_object_thunk_fn *fn = lazy_obj->thunk.fn; 18 | void *arg = lazy_obj->thunk.arg; 19 | drgn_object_init(&lazy_obj->obj, prog); 20 | err = fn(&lazy_obj->obj, arg); 21 | if (err) { 22 | /* Oops, revert back to a thunk so it can be retried. */ 23 | drgn_lazy_object_init_thunk(lazy_obj, prog, fn, arg); 24 | return err; 25 | } 26 | } 27 | return NULL; 28 | } 29 | 30 | void drgn_lazy_object_deinit(union drgn_lazy_object *lazy_obj) 31 | { 32 | if (drgn_lazy_object_is_evaluated(lazy_obj)) 33 | drgn_object_deinit(&lazy_obj->obj); 34 | else 35 | lazy_obj->thunk.fn(NULL, lazy_obj->thunk.arg); 36 | } 37 | 38 | struct drgn_error * 39 | drgn_lazy_object_check_prog(const union drgn_lazy_object *lazy_obj, 40 | struct drgn_program *prog) 41 | { 42 | if ((drgn_lazy_object_is_evaluated(lazy_obj) ? 43 | drgn_object_program(&lazy_obj->obj) : lazy_obj->thunk.prog) 44 | != prog) { 45 | return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, 46 | "object is from different program"); 47 | } 48 | return NULL; 49 | } 50 | -------------------------------------------------------------------------------- /libdrgn/lexer.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include "drgn_internal.h" 5 | #include "lexer.h" 6 | 7 | DEFINE_VECTOR_FUNCTIONS(drgn_token_vector); 8 | 9 | void drgn_lexer_init(struct drgn_lexer *lexer, drgn_lexer_func func, 10 | const char *str) 11 | { 12 | lexer->func = func; 13 | lexer->p = str; 14 | drgn_token_vector_init(&lexer->stack); 15 | } 16 | 17 | void drgn_lexer_deinit(struct drgn_lexer *lexer) 18 | { 19 | drgn_token_vector_deinit(&lexer->stack); 20 | } 21 | 22 | struct drgn_error *drgn_lexer_pop(struct drgn_lexer *lexer, 23 | struct drgn_token *token) 24 | { 25 | if (drgn_token_vector_empty(&lexer->stack)) { 26 | return lexer->func(lexer, token); 27 | } else { 28 | *token = *drgn_token_vector_pop(&lexer->stack); 29 | return NULL; 30 | } 31 | } 32 | 33 | struct drgn_error *drgn_lexer_push(struct drgn_lexer *lexer, 34 | const struct drgn_token *token) 35 | { 36 | if (!drgn_token_vector_append(&lexer->stack, token)) 37 | return &drgn_enomem; 38 | return NULL; 39 | } 40 | 41 | struct drgn_error *drgn_lexer_peek(struct drgn_lexer *lexer, 42 | struct drgn_token *token) 43 | { 44 | struct drgn_error *err; 45 | 46 | err = drgn_lexer_pop(lexer, token); 47 | if (!err) 48 | err = drgn_lexer_push(lexer, token); 49 | return err; 50 | } 51 | -------------------------------------------------------------------------------- /libdrgn/linux_kernel.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #ifndef DRGN_LINUX_KERNEL_H 5 | #define DRGN_LINUX_KERNEL_H 6 | 7 | #include "drgn_internal.h" 8 | 9 | struct drgn_debug_info_options; 10 | struct drgn_standard_debug_info_find_state; 11 | 12 | struct drgn_error *drgn_program_finish_set_kernel(struct drgn_program *prog); 13 | 14 | struct drgn_error *read_memory_via_pgtable(void *buf, uint64_t address, 15 | size_t count, uint64_t offset, 16 | void *arg, bool physical); 17 | 18 | struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog, 19 | const char *desc, 20 | size_t descsz); 21 | 22 | struct drgn_error *proc_kallsyms_symbol_addr(const char *name, 23 | unsigned long *ret); 24 | 25 | struct drgn_error *read_vmcoreinfo_fallback(struct drgn_program *prog); 26 | 27 | struct drgn_error * 28 | linux_kernel_loaded_module_iterator_create(struct drgn_program *prog, 29 | struct drgn_module_iterator **ret); 30 | 31 | struct drgn_error * 32 | drgn_module_try_vmlinux_files(struct drgn_module *module, 33 | const struct drgn_debug_info_options *options); 34 | 35 | struct drgn_error * 36 | drgn_module_try_linux_kmod_files(struct drgn_module *module, 37 | const struct drgn_debug_info_options *options, 38 | struct drgn_standard_debug_info_find_state *state); 39 | 40 | #define KDUMP_SIGNATURE "KDUMP " 41 | #define KDUMP_SIG_LEN (sizeof(KDUMP_SIGNATURE) - 1) 42 | 43 | #define FLATTENED_SIGNATURE "makedumpfile" 44 | #define FLATTENED_SIG_LEN (sizeof(FLATTENED_SIGNATURE) - 1) 45 | 46 | #ifdef WITH_LIBKDUMPFILE 47 | struct drgn_error *drgn_program_cache_kdump_threads(struct drgn_program *prog); 48 | struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog); 49 | #else 50 | static inline struct drgn_error * 51 | drgn_program_set_kdump(struct drgn_program *prog) 52 | { 53 | return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT, 54 | "drgn was built without libkdumpfile support"); 55 | } 56 | #endif 57 | 58 | #endif /* DRGN_LINUX_KERNEL_H */ 59 | -------------------------------------------------------------------------------- /libdrgn/linux_kernel_object_find.inc.strswitch: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | static struct drgn_error * 5 | linux_kernel_object_find(const char *name, size_t name_len, 6 | const char *filename, 7 | enum drgn_find_object_flags flags, void *arg, 8 | struct drgn_object *ret) 9 | { 10 | struct drgn_program *prog = arg; 11 | if (!filename) { 12 | @memswitch (name, name_len)@ 13 | @case "PAGE_SHIFT"@ 14 | if (flags & DRGN_FIND_OBJECT_CONSTANT) 15 | return linux_kernel_get_page_shift(prog, ret); 16 | break; 17 | @case "PAGE_SIZE"@ 18 | if (flags & DRGN_FIND_OBJECT_CONSTANT) 19 | return linux_kernel_get_page_size(prog, ret); 20 | break; 21 | @case "PAGE_MASK"@ 22 | if (flags & DRGN_FIND_OBJECT_CONSTANT) 23 | return linux_kernel_get_page_mask(prog, ret); 24 | break; 25 | @case "UTS_RELEASE"@ 26 | if (flags & DRGN_FIND_OBJECT_CONSTANT) 27 | return linux_kernel_get_uts_release(prog, ret); 28 | break; 29 | @case "VMCOREINFO"@ 30 | if (flags & DRGN_FIND_OBJECT_CONSTANT) 31 | return linux_kernel_get_vmcoreinfo(prog, ret); 32 | break; 33 | @case "jiffies"@ 34 | if (flags & DRGN_FIND_OBJECT_VARIABLE) 35 | return linux_kernel_get_jiffies(prog, ret); 36 | break; 37 | @case "vmemmap"@ 38 | if (flags & DRGN_FIND_OBJECT_CONSTANT) 39 | return linux_kernel_get_vmemmap(prog, ret); 40 | break; 41 | @endswitch@ 42 | } 43 | return &drgn_not_found; 44 | } 45 | -------------------------------------------------------------------------------- /libdrgn/m4/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !/.gitignore 3 | !/ax_append_compile_flags.m4 4 | !/ax_append_flag.m4 5 | !/ax_check_compile_flag.m4 6 | !/ax_require_defined.m4 7 | !/my_c_auto.m4 8 | !/my_c_switch_enum.m4 9 | !/my_check_va_args_comma_deletion.m4 10 | !/my_python_devel.m4 11 | -------------------------------------------------------------------------------- /libdrgn/m4/ax_append_compile_flags.m4: -------------------------------------------------------------------------------- 1 | # ============================================================================ 2 | # https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html 3 | # ============================================================================ 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # For every FLAG1, FLAG2 it is checked whether the compiler works with the 12 | # flag. If it does, the flag is added FLAGS-VARIABLE 13 | # 14 | # If FLAGS-VARIABLE is not specified, the current language's flags (e.g. 15 | # CFLAGS) is used. During the check the flag is always added to the 16 | # current language's flags. 17 | # 18 | # If EXTRA-FLAGS is defined, it is added to the current language's default 19 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 20 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 21 | # force the compiler to issue an error when a bad flag is given. 22 | # 23 | # INPUT gives an alternative input source to AC_COMPILE_IFELSE. 24 | # 25 | # NOTE: This macro depends on the AX_APPEND_FLAG and 26 | # AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with 27 | # AX_APPEND_LINK_FLAGS. 28 | # 29 | # LICENSE 30 | # 31 | # Copyright (c) 2011 Maarten Bosmans 32 | # 33 | # Copying and distribution of this file, with or without modification, are 34 | # permitted in any medium without royalty provided the copyright notice 35 | # and this notice are preserved. This file is offered as-is, without any 36 | # warranty. 37 | 38 | #serial 7 39 | 40 | AC_DEFUN([AX_APPEND_COMPILE_FLAGS], 41 | [AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) 42 | AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) 43 | for flag in $1; do 44 | AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4]) 45 | done 46 | ])dnl AX_APPEND_COMPILE_FLAGS 47 | -------------------------------------------------------------------------------- /libdrgn/m4/ax_append_flag.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_append_flag.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # FLAG is appended to the FLAGS-VARIABLE shell variable, with a space 12 | # added in between. 13 | # 14 | # If FLAGS-VARIABLE is not specified, the current language's flags (e.g. 15 | # CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains 16 | # FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly 17 | # FLAG. 18 | # 19 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. 20 | # 21 | # LICENSE 22 | # 23 | # Copyright (c) 2008 Guido U. Draheim 24 | # Copyright (c) 2011 Maarten Bosmans 25 | # 26 | # Copying and distribution of this file, with or without modification, are 27 | # permitted in any medium without royalty provided the copyright notice 28 | # and this notice are preserved. This file is offered as-is, without any 29 | # warranty. 30 | 31 | #serial 8 32 | 33 | AC_DEFUN([AX_APPEND_FLAG], 34 | [dnl 35 | AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF 36 | AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) 37 | AS_VAR_SET_IF(FLAGS,[ 38 | AS_CASE([" AS_VAR_GET(FLAGS) "], 39 | [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], 40 | [ 41 | AS_VAR_APPEND(FLAGS,[" $1"]) 42 | AC_RUN_LOG([: FLAGS="$FLAGS"]) 43 | ]) 44 | ], 45 | [ 46 | AS_VAR_SET(FLAGS,[$1]) 47 | AC_RUN_LOG([: FLAGS="$FLAGS"]) 48 | ]) 49 | AS_VAR_POPDEF([FLAGS])dnl 50 | ])dnl AX_APPEND_FLAG 51 | -------------------------------------------------------------------------------- /libdrgn/m4/ax_check_compile_flag.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check whether the given FLAG works with the current language's compiler 12 | # or gives an error. (Warnings, however, are ignored) 13 | # 14 | # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on 15 | # success/failure. 16 | # 17 | # If EXTRA-FLAGS is defined, it is added to the current language's default 18 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 19 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 20 | # force the compiler to issue an error when a bad flag is given. 21 | # 22 | # INPUT gives an alternative input source to AC_COMPILE_IFELSE. 23 | # 24 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this 25 | # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. 26 | # 27 | # LICENSE 28 | # 29 | # Copyright (c) 2008 Guido U. Draheim 30 | # Copyright (c) 2011 Maarten Bosmans 31 | # 32 | # Copying and distribution of this file, with or without modification, are 33 | # permitted in any medium without royalty provided the copyright notice 34 | # and this notice are preserved. This file is offered as-is, without any 35 | # warranty. 36 | 37 | #serial 6 38 | 39 | AC_DEFUN([AX_CHECK_COMPILE_FLAG], 40 | [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF 41 | AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl 42 | AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ 43 | ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS 44 | _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" 45 | AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], 46 | [AS_VAR_SET(CACHEVAR,[yes])], 47 | [AS_VAR_SET(CACHEVAR,[no])]) 48 | _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) 49 | AS_VAR_IF(CACHEVAR,yes, 50 | [m4_default([$2], :)], 51 | [m4_default([$3], :)]) 52 | AS_VAR_POPDEF([CACHEVAR])dnl 53 | ])dnl AX_CHECK_COMPILE_FLAGS 54 | -------------------------------------------------------------------------------- /libdrgn/m4/ax_require_defined.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_require_defined.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_REQUIRE_DEFINED(MACRO) 8 | # 9 | # DESCRIPTION 10 | # 11 | # AX_REQUIRE_DEFINED is a simple helper for making sure other macros have 12 | # been defined and thus are available for use. This avoids random issues 13 | # where a macro isn't expanded. Instead the configure script emits a 14 | # non-fatal: 15 | # 16 | # ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found 17 | # 18 | # It's like AC_REQUIRE except it doesn't expand the required macro. 19 | # 20 | # Here's an example: 21 | # 22 | # AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) 23 | # 24 | # LICENSE 25 | # 26 | # Copyright (c) 2014 Mike Frysinger 27 | # 28 | # Copying and distribution of this file, with or without modification, are 29 | # permitted in any medium without royalty provided the copyright notice 30 | # and this notice are preserved. This file is offered as-is, without any 31 | # warranty. 32 | 33 | #serial 2 34 | 35 | AC_DEFUN([AX_REQUIRE_DEFINED], [dnl 36 | m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) 37 | ])dnl AX_REQUIRE_DEFINED 38 | -------------------------------------------------------------------------------- /libdrgn/m4/my_c_auto.m4: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | # Check whether C23 auto is supported by the current compiler and compilation 5 | # flags. If not, but the GNU __auto_type extension is supported, define auto to 6 | # __auto_type. Otherwise, fail. 7 | AC_DEFUN([MY_C_AUTO], 8 | [AC_CACHE_CHECK([for auto], [my_cv_c_auto], 9 | [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[auto x = 1;]])], 10 | [my_cv_c_auto=yes], [my_cv_c_auto=no])]) 11 | if test "x$my_cv_c_auto" != xyes; then 12 | AC_CACHE_CHECK([for __auto_type], [my_cv_c___auto_type], 13 | [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[__auto_type x = 1;]])], 14 | [my_cv_c___auto_type=yes], [my_cv_c___auto_type=no])]) 15 | if test "x$my_cv_c___auto_type" = xyes; then 16 | AC_DEFINE([auto], [__auto_type]) 17 | else 18 | AC_MSG_ERROR([no auto or __auto_type]) 19 | fi 20 | fi 21 | ]) 22 | -------------------------------------------------------------------------------- /libdrgn/m4/my_check_va_args_comma_deletion.m4: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | # Check that when ', ##__VA_ARGS__' is used in a macro taking variable 5 | # arguments, the comma is deleted if no variable arguments were passed. This is 6 | # a GNU C extension: https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html. 7 | AC_DEFUN([MY_CHECK_VA_ARGS_COMMA_DELETION], 8 | [AC_CACHE_CHECK([for __VA_ARGS__ comma deletion extension], 9 | [my_cv_va_args_comma_deletion], 10 | [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ 11 | #define mandatory(_, ...) mandatory2(, ##__VA_ARGS__, 0, 1) 12 | #define mandatory2(_0, _1, N, ...) N 13 | _Static_assert(mandatory(_), "no comma deletion with mandatory argument"); 14 | 15 | #define empty(...) empty2(, ##__VA_ARGS__, 0, 1) 16 | #define empty2(_0, _1, N, ...) N 17 | _Static_assert(empty(), "no comma deletion with empty argument"); 18 | ]])], 19 | [my_cv_va_args_comma_deletion=yes], 20 | [my_cv_va_args_comma_deletion=no])]) 21 | if test "x$my_cv_va_args_comma_deletion" != xyes; then 22 | AC_MSG_FAILURE([no __VA_ARGS__ comma deletion extension]) 23 | fi 24 | ]) 25 | -------------------------------------------------------------------------------- /libdrgn/minmax.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * Minimum/maximum operations. 8 | * 9 | * See @ref MinMaxOperations. 10 | */ 11 | 12 | #ifndef DRGN_MINMAX_H 13 | #define DRGN_MINMAX_H 14 | 15 | #include "pp.h" 16 | 17 | /** 18 | * @ingroup Internals 19 | * 20 | * @defgroup MinMaxOperations Minimum/maximum operations 21 | * 22 | * Generic minimum/maximum operations. 23 | * 24 | * @{ 25 | */ 26 | 27 | /** Get the minimum of two expressions with compatible types. */ 28 | #define min(x, y) cmp_once_impl(x, y, PP_UNIQUE(_x), PP_UNIQUE(_y), <) 29 | /** Get the maximum of two expressions with compatible types. */ 30 | #define max(x, y) cmp_once_impl(x, y, PP_UNIQUE(_x), PP_UNIQUE(_y), >) 31 | /** @cond */ 32 | #define cmp_once_impl(x, y, unique_x, unique_y, op) ({ \ 33 | __auto_type unique_x = (x); \ 34 | __auto_type unique_y = (y); \ 35 | /* Generate a warning if x and y do not have compatible types. */ \ 36 | (void)(&unique_x == &unique_y); \ 37 | unique_x op unique_y ? unique_x : unique_y; \ 38 | }) 39 | /** @endcond */ 40 | 41 | /** 42 | * Get the minimum of two integer constant expressions, resulting in an integer 43 | * constant expression. 44 | */ 45 | #define min_iconst(x, y) cmp_iconst_impl(x, y, <) 46 | /** 47 | * Get the maximum of two integer constant expressions, resulting in an integer 48 | * constant expression. 49 | */ 50 | #define max_iconst(x, y) cmp_iconst_impl(x, y, >) 51 | /** @cond */ 52 | #define cmp_iconst_impl(x, y, op) \ 53 | /* \ 54 | * Enforce that the arguments are integer constant expressions. The \ 55 | * size of a non-VLA array must be an integer constant expression, and \ 56 | * a compound literal cannot be a VLA. Evaluates to non-zero to fall \ 57 | * through to the comparison. \ 58 | */ \ 59 | (sizeof((char [(x) * 0 + (y) * 0 + 1]){0}) && \ 60 | (x) op (y) ? (x) : (y)) 61 | /** @endcond */ 62 | 63 | /** @} */ 64 | 65 | #endif /* DRGN_MINMAX_H */ 66 | -------------------------------------------------------------------------------- /libdrgn/no_python.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | // Fallback implementations for builds without Python support. 5 | 6 | #include "plugins.h" 7 | #include "program.h" 8 | 9 | LIBDRGN_PUBLIC struct drgn_error * 10 | drgn_program_create(const struct drgn_platform *platform, 11 | struct drgn_program **ret) 12 | { 13 | struct drgn_program *prog = malloc(sizeof(*prog)); 14 | if (!prog) 15 | return &drgn_enomem; 16 | drgn_program_init(prog, platform); 17 | *ret = prog; 18 | return NULL; 19 | } 20 | 21 | LIBDRGN_PUBLIC void drgn_program_destroy(struct drgn_program *prog) 22 | { 23 | if (prog) { 24 | drgn_program_deinit(prog); 25 | free(prog); 26 | } 27 | } 28 | 29 | void drgn_call_plugins_prog(const char *name, struct drgn_program *prog) 30 | { 31 | } 32 | 33 | void *drgn_begin_blocking(void) 34 | { 35 | return NULL; 36 | } 37 | 38 | void drgn_end_blocking(void *state) 39 | { 40 | } 41 | -------------------------------------------------------------------------------- /libdrgn/nstring.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * String with length. 8 | */ 9 | 10 | #ifndef DRGN_NSTRING_H 11 | #define DRGN_NSTRING_H 12 | 13 | #include 14 | 15 | /** A string with a stored length. */ 16 | struct nstring { 17 | /** 18 | * The string, which is not necessarily null-terminated and may have 19 | * embedded null bytes. 20 | */ 21 | const char *str; 22 | /** The length in bytes of the string. */ 23 | size_t len; 24 | }; 25 | 26 | /** Compare two @ref nstring keys for equality. */ 27 | static inline bool nstring_eq(const struct nstring *a, const struct nstring *b) 28 | { 29 | /* 30 | * len == 0 is a special case because memcmp(NULL, NULL, 0) is 31 | * technically undefined. 32 | */ 33 | return (a->len == b->len && 34 | (a->len == 0 || memcmp(a->str, b->str, a->len) == 0)); 35 | } 36 | 37 | #endif /* DRGN_NSTRING_H */ 38 | -------------------------------------------------------------------------------- /libdrgn/openmp.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #ifndef DRGN_OPENMP_H 5 | #define DRGN_OPENMP_H 6 | 7 | #ifdef _OPENMP 8 | #include // IWYU pragma: export 9 | 10 | extern int drgn_num_threads; 11 | void drgn_init_num_threads(void); 12 | #else 13 | static inline int omp_get_thread_num(void) 14 | { 15 | return 0; 16 | } 17 | 18 | #define drgn_num_threads 1 19 | static inline void drgn_init_num_threads(void) 20 | { 21 | } 22 | #endif 23 | 24 | #endif /* DRGN_OPENMP_H */ 25 | -------------------------------------------------------------------------------- /libdrgn/plugins.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #ifndef DRGN_PLUGINS_H 5 | #define DRGN_PLUGINS_H 6 | 7 | struct drgn_program; 8 | 9 | void drgn_call_plugins_prog(const char *name, struct drgn_program *prog); 10 | 11 | #endif /* DRGN_PLUGINS_H */ 12 | -------------------------------------------------------------------------------- /libdrgn/python/plugins.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include "drgnpy.h" 5 | #include "../plugins.h" 6 | 7 | void drgn_call_plugins_prog(const char *name, struct drgn_program *prog) 8 | { 9 | PyGILState_guard(); 10 | 11 | static PyObject *call_plugins; 12 | if (!call_plugins) { 13 | _cleanup_pydecref_ PyObject *_drgn_util_plugins_module = 14 | PyImport_ImportModule("_drgn_util.plugins"); 15 | if (!_drgn_util_plugins_module) { 16 | PyErr_WriteUnraisable(NULL); 17 | return; 18 | } 19 | call_plugins = PyObject_GetAttrString(_drgn_util_plugins_module, 20 | "call_plugins"); 21 | if (!call_plugins) { 22 | PyErr_WriteUnraisable(NULL); 23 | return; 24 | } 25 | } 26 | 27 | Program *prog_obj = container_of(prog, Program, prog); 28 | _cleanup_pydecref_ PyObject *res = 29 | PyObject_CallFunction(call_plugins, "sO", name, prog_obj); 30 | if (!res) 31 | PyErr_WriteUnraisable(call_plugins); 32 | } 33 | -------------------------------------------------------------------------------- /libdrgn/python/test.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /* 5 | * Wrapper functions for testing. 6 | * 7 | * In order to test a few internal interfaces that don't have Python bindings, 8 | * we export some wrappers for those interfaces. These wrappers are accessed via 9 | * ctypes. 10 | * 11 | * The extra declarations are needed to silence -Wmissing-prototypes. 12 | */ 13 | 14 | #include "drgnpy.h" 15 | #include "../serialize.h" 16 | 17 | typeof(serialize_bits) drgn_test_serialize_bits; 18 | DRGNPY_PUBLIC void drgn_test_serialize_bits(void *buf, uint64_t bit_offset, 19 | uint64_t uvalue, uint8_t bit_size, 20 | bool little_endian) 21 | { 22 | return serialize_bits(buf, bit_offset, uvalue, bit_size, little_endian); 23 | } 24 | 25 | typeof(deserialize_bits) drgn_test_deserialize_bits; 26 | DRGNPY_PUBLIC uint64_t drgn_test_deserialize_bits(const void *buf, 27 | uint64_t bit_offset, 28 | uint8_t bit_size, 29 | bool little_endian) 30 | { 31 | return deserialize_bits(buf, bit_offset, bit_size, little_endian); 32 | } 33 | -------------------------------------------------------------------------------- /libdrgn/stack_trace.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | /** 5 | * @file 6 | * 7 | * Stack trace internals 8 | * 9 | * See @ref StackTraceInternals. 10 | */ 11 | 12 | #ifndef DRGN_STACK_TRACE_H 13 | #define DRGN_STACK_TRACE_H 14 | 15 | #include 16 | #include 17 | 18 | /** 19 | * @ingroup Internals 20 | * 21 | * @defgroup StackTraceInternals Stack traces 22 | * 23 | * Stack trace internals. 24 | * 25 | * This provides the internal data structures used for stack traces. 26 | * 27 | * @{ 28 | */ 29 | 30 | struct drgn_stack_frame { 31 | struct drgn_register_state *regs; 32 | Dwarf_Die *scopes; 33 | size_t num_scopes; 34 | size_t function_scope; 35 | }; 36 | 37 | struct drgn_stack_trace { 38 | struct drgn_program *prog; 39 | size_t num_frames; 40 | struct drgn_stack_frame frames[]; 41 | }; 42 | 43 | /** @} */ 44 | 45 | #endif /* DRGN_STACK_TRACE_H */ 46 | -------------------------------------------------------------------------------- /libdrgn/tests/binary_search.c.in: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include 5 | #include "../binary_search.h" 6 | 7 | #suite binary_search 8 | 9 | #test-loop(0, 6) binary_search_ge_basic 10 | { 11 | const int arr[] = { 10, 20, 30, 40, 50 }; 12 | ck_assert_uint_eq(binary_search_ge(arr, _i, &(int){5}, scalar_less), 0); 13 | for (int i = 1; i <= _i; i++) { 14 | ck_assert_uint_eq(binary_search_ge(arr, _i, &(int){i * 10}, 15 | scalar_less), i - 1); 16 | ck_assert_uint_eq(binary_search_ge(arr, _i, &(int){i * 10 + 5}, 17 | scalar_less), i); 18 | } 19 | } 20 | 21 | #test binary_search_ge_duplicates 22 | { 23 | const int arr[] = { 10, 10, 10 }; 24 | ck_assert_uint_eq(binary_search_ge(arr, 3, &(int){10}, scalar_less), 0); 25 | } 26 | 27 | #test binary_search_ge_custom_less 28 | { 29 | const struct { 30 | int value; 31 | const char *str; 32 | } arr[] = { 33 | { 10, "ten" }, 34 | { 20, "twenty" }, 35 | }; 36 | #define my_less(a, b) ((a)->value < *(b)) 37 | ck_assert_uint_eq(binary_search_ge(arr, 2, &(int){20}, my_less), 1); 38 | #undef my_less 39 | } 40 | 41 | #test-loop(0, 6) binary_search_gt_basic 42 | { 43 | const int arr[] = { 10, 20, 30, 40, 50 }; 44 | ck_assert_uint_eq(binary_search_gt(arr, _i, &(int){5}, scalar_less), 0); 45 | for (int i = 1; i <= _i; i++) { 46 | ck_assert_uint_eq(binary_search_gt(arr, _i, &(int){i * 10}, 47 | scalar_less), i); 48 | ck_assert_uint_eq(binary_search_gt(arr, _i, &(int){i * 10 + 5}, 49 | scalar_less), i); 50 | } 51 | } 52 | 53 | #test binary_search_gt_duplicates 54 | { 55 | const int arr[] = { 10, 10, 10 }; 56 | ck_assert_uint_eq(binary_search_gt(arr, 3, &(int){10}, scalar_less), 3); 57 | } 58 | 59 | #test binary_search_gt_custom_less 60 | { 61 | const struct { 62 | int value; 63 | const char *str; 64 | } arr[] = { 65 | { 10, "ten" }, 66 | { 20, "twenty" }, 67 | }; 68 | #define my_less(a, b) (*(a) < (b)->value) 69 | ck_assert_uint_eq(binary_search_gt(arr, 2, &(int){20}, my_less), 2); 70 | #undef my_less 71 | } 72 | -------------------------------------------------------------------------------- /libdrgn/tests/crc32.c.in: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include 5 | 6 | #include "test_util.h" 7 | #include "../crc32.h" 8 | 9 | static uint32_t string_crc32(const char *s) 10 | { 11 | return ~crc32_update(~0, s, strlen(s)); 12 | } 13 | 14 | #suite crc32 15 | 16 | #tcase crc32 17 | 18 | #test empty 19 | { 20 | ck_assert_uint_eq(string_crc32(""), 0); 21 | } 22 | 23 | #test simple 24 | { 25 | // https://reveng.sourceforge.io/crc-catalogue/17plus.htm#crc.cat.crc-32-iso-hdlc 26 | ck_assert_uint_eq(string_crc32("123456789"), 0xcbf43926); 27 | // http://www.febooti.com/products/filetweak/members/hash-and-crc/test-vectors/ 28 | ck_assert_uint_eq(string_crc32("The quick brown fox jumps over the lazy dog"), 29 | 0x414fa339); 30 | } 31 | 32 | #test update 33 | { 34 | uint32_t crc = ~0; 35 | crc = crc32_update(crc, "12", 2); 36 | crc = crc32_update(crc, "345", 3); 37 | crc = crc32_update(crc, "6789", 4); 38 | crc = ~crc; 39 | ck_assert_uint_eq(crc, 0xcbf43926); 40 | } 41 | -------------------------------------------------------------------------------- /libdrgn/tests/hexlify.c.in: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include 5 | 6 | #include "test_util.h" 7 | #include "../cleanup.h" 8 | #include "../hexlify.h" 9 | 10 | static const uint8_t binary[] = { 11 | 0x19, 0x29, 0x1d, 0x9a, 0xc4, 0xf3, 0x4c, 0x42, 0x01, 0xee, 12 | 0xdf, 0x9e, 0x8d, 0x1e, 0x59, 0x68, 0xf7, 0xd5, 0x48, 0x19, 13 | }; 14 | static const char str[] = "19291d9ac4f34c4201eedf9e8d1e5968f7d54819"; 15 | 16 | #suite hexlify 17 | 18 | #tcase hexlify 19 | 20 | #test hexlify_simple 21 | { 22 | char out[2 * sizeof(binary) + 1]; 23 | out[sizeof(out) - 1] = '~'; 24 | hexlify(binary, sizeof(binary), out); 25 | ck_assert_mem_eq(out, str, sizeof(out) - 1); 26 | // Test that the string wasn't null-terminated. 27 | ck_assert_int_eq(out[sizeof(out) - 1], '~'); 28 | } 29 | 30 | #test ahexlify_simple 31 | { 32 | _cleanup_free_ char *out = ahexlify(binary, sizeof(binary)); 33 | ck_assert_ptr_nonnull(out); 34 | ck_assert_str_eq(out, str); 35 | } 36 | 37 | #test unhexlify_simple 38 | { 39 | uint8_t out[(sizeof(str) - 1) / 2]; 40 | ck_assert(unhexlify(str, sizeof(str) - 1, out)); 41 | ck_assert_mem_eq(out, binary, sizeof(binary)); 42 | } 43 | 44 | #test unhexlify_odd 45 | { 46 | ck_assert(!unhexlify("abc", 3, (uint8_t [1]){})); 47 | } 48 | 49 | #test unhexlify_non_hex 50 | { 51 | ck_assert(!unhexlify("foobar", 6, (uint8_t [3]){})); 52 | } 53 | -------------------------------------------------------------------------------- /libdrgn/tests/recursion_guard.c.in: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include "test_util.h" 5 | #include "../error.h" 6 | 7 | static struct drgn_error *no_recursion_allowed(int *counter) 8 | { 9 | drgn_recursion_guard(0, "recursive call detected"); 10 | (*counter)++; 11 | return no_recursion_allowed(counter); 12 | } 13 | 14 | static struct drgn_error *limited_recursion_allowed(int *counter) 15 | { 16 | drgn_recursion_guard(10, "maximum recursion depth exceeded"); 17 | (*counter)++; 18 | return limited_recursion_allowed(counter); 19 | } 20 | 21 | #suite recursion_guard 22 | 23 | #test no_recursion 24 | { 25 | int counter = 0; 26 | struct drgn_error *err = no_recursion_allowed(&counter); 27 | ck_assert_ptr_nonnull(err); 28 | ck_assert_int_eq(err->code, DRGN_ERROR_RECURSION); 29 | ck_assert_str_eq(err->message, "recursive call detected"); 30 | drgn_error_destroy(err); 31 | ck_assert_int_eq(counter, 1); 32 | } 33 | 34 | #test limited_recursion 35 | { 36 | int counter = 0; 37 | struct drgn_error *err = limited_recursion_allowed(&counter); 38 | ck_assert_ptr_nonnull(err); 39 | ck_assert_int_eq(err->code, DRGN_ERROR_RECURSION); 40 | ck_assert_str_eq(err->message, "maximum recursion depth exceeded"); 41 | drgn_error_destroy(err); 42 | ck_assert_int_eq(counter, 11); 43 | } 44 | -------------------------------------------------------------------------------- /libdrgn/util.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Oracle and/or its affiliates 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | #include "util.h" 4 | 5 | static _Thread_local int (*qsort_arg_compar)(const void *, const void *, void*); 6 | static _Thread_local void *qsort_arg_arg; 7 | 8 | static int qsort_arg_compar_wrapper(const void *a, const void *b) 9 | { 10 | return qsort_arg_compar(a, b, qsort_arg_arg); 11 | } 12 | 13 | void qsort_arg(void *base, size_t nmemb, size_t size, 14 | int (*compar)(const void *, const void *, void*), void *arg) 15 | { 16 | qsort_arg_compar = compar; 17 | qsort_arg_arg = arg; 18 | qsort(base, nmemb, size, qsort_arg_compar_wrapper); 19 | } 20 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.isort] 2 | profile = "black" 3 | known_first_party = ["drgn", "_drgn", "drgndoc", "vmtest"] 4 | combine_as_imports = true 5 | force_sort_within_sections = true 6 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_classes = 3 | python_functions = 4 | -------------------------------------------------------------------------------- /scripts/build_dists.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | set -eux 6 | 7 | : "${PYTHON=python3}" 8 | "$PYTHON" setup.py sdist 9 | SDIST=dist/drgn-"$("$PYTHON" setup.py --version)".tar.gz 10 | 11 | if [ "${DOCKER+set}" = set ]; then 12 | PODMAN="$DOCKER" 13 | PODMAN_OPTS="--env OWNER=$(id -u):$(id -g)" 14 | else 15 | PODMAN=podman 16 | PODMAN_OPTS="--security-opt label=disable" 17 | fi 18 | 19 | $PODMAN run -it \ 20 | --env PLAT=manylinux2014_x86_64 \ 21 | --env SDIST="$SDIST" \ 22 | $PODMAN_OPTS \ 23 | --volume "$(pwd)":/io:ro \ 24 | --volume "$(pwd)/dist":/io/dist \ 25 | --workdir /io \ 26 | --hostname drgn \ 27 | --rm \ 28 | --pull always \ 29 | quay.io/pypa/manylinux2014_x86_64 \ 30 | ./scripts/build_manylinux_in_docker.sh "${1:-}" 31 | -------------------------------------------------------------------------------- /scripts/crashme/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | # Makefile used to generate tests/resources/crashme* 5 | 6 | .PHONY: all cores clean 7 | 8 | .DELETE_ON_ERROR: 9 | 10 | EXECUTABLES := crashme crashme_pie crashme_static crashme_static_pie 11 | CORES := $(addsuffix .core, $(EXECUTABLES)) $(addsuffix _no_headers.core, $(EXECUTABLES)) 12 | BINARIES := crashme.so $(EXECUTABLES) crashme.dwz crashme.so.dwz crashme.alt 13 | ZSTD_BINARIES := $(addsuffix .zst, $(BINARIES)) 14 | ZSTD_CORES := $(addsuffix .zst, $(CORES)) 15 | 16 | all: $(BINARIES) cores $(ZSTD_BINARIES) $(ZSTD_CORES) 17 | 18 | clean: 19 | rm -f $(BINARIES) $(CORES) $(ZSTD_BINARIES) $(ZSTD_CORES) 20 | 21 | crashme.so: crashme.c common.c 22 | gcc -g -Os -fpic -shared $^ -o $@ 23 | 24 | crashme: main.c common.c crashme.so 25 | gcc -g -Os -fno-pie -no-pie $(filter-out crashme.so,$^) -o $@ -L . -l:crashme.so -Wl,-rpath,$(CURDIR) 26 | 27 | crashme_pie: main.c common.c crashme.so 28 | gcc -g -Os -fpie -pie $(filter-out crashme.so,$^) -o $@ -L . -l:crashme.so -Wl,-rpath,$(CURDIR) 29 | 30 | crashme_static: main.c common.c crashme.c 31 | musl-gcc -g -Os -fno-pie -static $^ -o $@ 32 | 33 | crashme_static_pie: main.c common.c crashme.c 34 | musl-gcc -g -Os -fpie -static-pie $^ -o $@ 35 | 36 | crashme.dwz crashme.so.dwz crashme.alt &: crashme crashme.so 37 | cp crashme crashme.dwz 38 | cp crashme.so crashme.so.dwz 39 | dwz -m crashme.alt -r crashme.dwz crashme.so.dwz 40 | 41 | cores: $(CORES) 42 | 43 | .NOTPARALLEL: cores 44 | 45 | define CORE_COMMAND 46 | flock /proc/sys/kernel/core_pattern sh -e -c '\ 47 | ulimit -c unlimited; \ 48 | echo "$$COREDUMP_FILTER" > /proc/$$$$/coredump_filter; \ 49 | old_pattern=$$(cat /proc/sys/kernel/core_pattern); \ 50 | restore_core_pattern() { \ 51 | echo "$$old_pattern" > /proc/sys/kernel/core_pattern; \ 52 | }; \ 53 | trap restore_core_pattern EXIT; \ 54 | echo "$$PWD/core.%p" > /proc/sys/kernel/core_pattern; \ 55 | su "$$SUDO_USER" -c "env -i sh -l -c \"exec ./$<\" & wait; mv core.\$$! $@"' 56 | endef 57 | 58 | %.core: % 59 | sudo env COREDUMP_FILTER=0x33 $(CORE_COMMAND) 60 | 61 | %_no_headers.core: % 62 | sudo env COREDUMP_FILTER=0x23 $(CORE_COMMAND) 63 | 64 | %.zst: % 65 | zstd -19 $< -o $@ 66 | -------------------------------------------------------------------------------- /scripts/crashme/common.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include "crashme.h" 5 | 6 | __attribute__((__visibility__("hidden"))) 7 | int *crashme_ptr(void) 8 | { 9 | return (int *)0xabc; 10 | } 11 | -------------------------------------------------------------------------------- /scripts/crashme/crashme.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include "crashme.h" 5 | 6 | __attribute__((__noipa__)) static int c(struct crashme *cm) 7 | { 8 | *cm->ptr = 0xdeadbeef; 9 | return 3; 10 | } 11 | 12 | __attribute__((__noipa__)) static int b(struct crashme *cm) 13 | { 14 | return c(cm) - 1; 15 | } 16 | 17 | __attribute__((__noipa__)) static int a(struct crashme *cm) 18 | { 19 | return b(cm) - 1; 20 | } 21 | 22 | int crashme(struct crashme *cm) 23 | { 24 | return cm->ptr == crashme_ptr() ? a(cm) - 1 : 1; 25 | } 26 | -------------------------------------------------------------------------------- /scripts/crashme/crashme.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #ifndef CRASHME_H 5 | #define CRASHME_H 6 | 7 | int *crashme_ptr(void); 8 | 9 | struct crashme { 10 | int *ptr; 11 | }; 12 | 13 | int crashme(struct crashme *cm); 14 | 15 | #endif /* CRASHME_H */ 16 | -------------------------------------------------------------------------------- /scripts/crashme/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | #include "crashme.h" 5 | 6 | int main(void) 7 | { 8 | struct crashme cm = { crashme_ptr() }; 9 | return !!crashme(&cm); 10 | } 11 | -------------------------------------------------------------------------------- /scripts/cscope.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | : ${PYTHON:=python3} 6 | cscope_args=(-bq -i-) 7 | 8 | python_include="$("$PYTHON" -c 'import sysconfig; print(sysconfig.get_path("include"))' 2>/dev/null)" 9 | if [[ -n $python_include ]] ; then 10 | cscope_args+=("-I$python_include") 11 | fi 12 | python_platinclude="$("$PYTHON" -c 'import sysconfig; print(sysconfig.get_path("platinclude"))' 2>/dev/null)" 13 | if [[ -n $python_platinclude && $python_platinclude != $python_include ]] ; then 14 | cscope_args+=("-I$python_platinclude") 15 | fi 16 | 17 | find libdrgn -name '*.[ch]' -o -name '*.[ch].in' | cscope "${cscope_args[@]}" 18 | -------------------------------------------------------------------------------- /scripts/gen_elf_py.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | import argparse 6 | import re 7 | import subprocess 8 | import sys 9 | 10 | 11 | def main() -> None: 12 | argparse.ArgumentParser(description="Generate elf.py from elf.h").parse_args() 13 | 14 | contents = subprocess.check_output( 15 | ["gcc", "-dD", "-E", "-"], 16 | input="#include \n", 17 | universal_newlines=True, 18 | ) 19 | 20 | enums = { 21 | name: [] 22 | for name in ( 23 | "ET", 24 | "PT", 25 | "SHF", 26 | "SHN", 27 | "SHT", 28 | "STB", 29 | "STT", 30 | "STV", 31 | ) 32 | } 33 | for match in re.finditer( 34 | r"^\s*#\s*define\s+(?P" 35 | + "|".join(enums) 36 | + r")_(?P\w+)\s+(?:(?P0x[0-9a-fA-F]+|[0-9]+)|(?:\(\s*1U?\s*<<\s*(?P[0-9]+)\s*\)))", 37 | contents, 38 | re.MULTILINE, 39 | ): 40 | enum = match.group("enum") 41 | name = match.group("name") 42 | if match.group("value"): 43 | value = int(match.group("value"), 0) 44 | else: 45 | value = 1 << int(match.group("bitshift"), 10) 46 | enums[enum].append((name, value)) 47 | 48 | f = sys.stdout 49 | f.write( 50 | """\ 51 | # Copyright (c) Meta Platforms, Inc. and affiliates. 52 | # SPDX-License-Identifier: LGPL-2.1-or-later 53 | # Generated by scripts/gen_elf_py.py. 54 | 55 | import enum 56 | """ 57 | ) 58 | for type_name, constants in enums.items(): 59 | assert constants 60 | enum_class = "IntFlag" if type_name == "SHF" else "IntEnum" 61 | f.write(f"\n\nclass {type_name}(enum.{enum_class}):\n") 62 | for name, value in constants: 63 | f.write(f" {name} = 0x{value:X}\n") 64 | 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /scripts/generate_page_flag_getters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | import argparse 6 | import re 7 | import sys 8 | 9 | if __name__ == "__main__": 10 | parser = argparse.ArgumentParser( 11 | description="Generate PageFlag() helpers from include/linux/page-flags.h" 12 | ) 13 | args = parser.parse_args() 14 | 15 | flags = { 16 | # PageUptodate() isn't defined with PAGEFLAG because it needs 17 | # additional memory barriers, but other than that it's the same. 18 | "Uptodate": "uptodate", 19 | } 20 | for match in re.finditer( 21 | r"\b(?:|__|TEST)PAGEFLAG\s*\(\s*(\w+)(? bool: 36 | """ 37 | Return whether the ``PG_{lname}`` flag is set on a page. 38 | 39 | :param page: ``struct page *`` 40 | """ 41 | try: 42 | flag = page.prog_["PG_{lname}"] 43 | except KeyError: 44 | return False 45 | return bool(page.flags & (1 << flag)) 46 | ''' 47 | ) 48 | print() 49 | print("# End generated by scripts/generate_page_flag_getters.py.") 50 | -------------------------------------------------------------------------------- /scripts/generate_primitive_type_spellings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | import itertools 6 | import sys 7 | 8 | SPELLINGS = [ 9 | ("DRGN_C_TYPE_VOID", ["void"]), 10 | ("DRGN_C_TYPE_CHAR", ["char"]), 11 | ("DRGN_C_TYPE_SIGNED_CHAR", ["signed char"]), 12 | ("DRGN_C_TYPE_UNSIGNED_CHAR", ["unsigned char"]), 13 | ("DRGN_C_TYPE_SHORT", ["short", "signed short", "short int", "signed short int"]), 14 | ("DRGN_C_TYPE_UNSIGNED_SHORT", ["unsigned short", "unsigned short int"]), 15 | ("DRGN_C_TYPE_INT", ["int", "signed", "signed int"]), 16 | ("DRGN_C_TYPE_UNSIGNED_INT", ["unsigned int", "unsigned"]), 17 | ("DRGN_C_TYPE_LONG", ["long", "signed long", "long int", "signed long int"]), 18 | ("DRGN_C_TYPE_UNSIGNED_LONG", ["unsigned long", "unsigned long int"]), 19 | ( 20 | "DRGN_C_TYPE_LONG_LONG", 21 | ["long long", "signed long long", "long long int", "signed long long int"], 22 | ), 23 | ( 24 | "DRGN_C_TYPE_UNSIGNED_LONG_LONG", 25 | ["unsigned long long", "unsigned long long int"], 26 | ), 27 | ("DRGN_C_TYPE_BOOL", ["_Bool"]), 28 | ("DRGN_C_TYPE_FLOAT", ["float"]), 29 | ("DRGN_C_TYPE_DOUBLE", ["double"]), 30 | ("DRGN_C_TYPE_LONG_DOUBLE", ["long double"]), 31 | ("DRGN_C_TYPE_SIZE_T", ["size_t"]), 32 | ("DRGN_C_TYPE_PTRDIFF_T", ["ptrdiff_t"]), 33 | ] 34 | 35 | 36 | if __name__ == "__main__": 37 | output_file = sys.stdout 38 | output_file.write("LIBDRGN_PUBLIC const char * const * const\n") 39 | output_file.write("drgn_primitive_type_spellings[DRGN_PRIMITIVE_TYPE_NUM] = {\n") 40 | for primitive, spellings in SPELLINGS: 41 | output_file.write(f"\t[{primitive}] = (const char * []){{") 42 | seen = set() 43 | for spelling in spellings: 44 | for permutation in itertools.permutations(spelling.split()): 45 | s = " ".join(permutation) 46 | if s not in seen: 47 | output_file.write(f' "{s}",') 48 | seen.add(s) 49 | output_file.write(" NULL, },\n") 50 | output_file.write("};\n") 51 | -------------------------------------------------------------------------------- /scripts/pp/gen_pp_cat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | import argparse 6 | 7 | if __name__ == "__main__": 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument("max", type=int) 10 | args = parser.parse_args() 11 | 12 | for i in range(2, args.max + 1): 13 | print( 14 | f"#define PP_CAT{str(i) if i > 2 else ''}(" 15 | + ", ".join(f"_{j}" for j in range(i)) 16 | + f") PP_CAT_I{i}(" 17 | + ", ".join(f"_{j}" for j in range(i)) 18 | + ")" 19 | ) 20 | print("/** @cond */") 21 | for i in range(2, args.max + 1): 22 | print( 23 | f"#define PP_CAT_I{i}(" 24 | + ", ".join(f"_{j}" for j in range(i)) 25 | + ") " 26 | + "##".join(f"_{j}" for j in range(i)) 27 | ) 28 | print("/** @endcond */") 29 | -------------------------------------------------------------------------------- /scripts/pp/gen_pp_map.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | import argparse 6 | 7 | if __name__ == "__main__": 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument("max", type=int) 10 | args = parser.parse_args() 11 | 12 | print( 13 | "#define PP_MAP(func, arg, ...) PP_OVERLOAD(PP_MAP_I, __VA_ARGS__)(func, arg, __VA_ARGS__)" 14 | ) 15 | print("/** @cond */") 16 | for i in range(args.max, 1, -1): 17 | print( 18 | f"#define PP_MAP_I{i}(func, arg, x, ...) func(arg, x) PP_MAP_I{i - 1}(func, arg, __VA_ARGS__)" 19 | ) 20 | print("#define PP_MAP_I1(func, arg, x) func(arg, x)") 21 | print("#define PP_MAP_I0(func, arg, x)") 22 | print("/** @endcond */") 23 | -------------------------------------------------------------------------------- /tests/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/helpers/__init__.py -------------------------------------------------------------------------------- /tests/helpers/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/helpers/common/__init__.py -------------------------------------------------------------------------------- /tests/libdrgn.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import ctypes 5 | 6 | import _drgn 7 | 8 | _drgn_cdll = ctypes.CDLL(_drgn.__file__) 9 | 10 | 11 | class _drgn_error(ctypes.Structure): 12 | _fields_ = [ 13 | ("code", ctypes.c_uint), 14 | ("errnum", ctypes.c_int), 15 | ("path", ctypes.c_char_p), 16 | ("msg", ctypes.c_char_p), 17 | ] 18 | 19 | 20 | class _drgn_type(ctypes.Structure): 21 | pass 22 | 23 | 24 | class _drgn_qualified_type(ctypes.Structure): 25 | _fields_ = [ 26 | ("type", ctypes.POINTER(_drgn_type)), 27 | ("qualifiers", ctypes.c_uint), 28 | ] 29 | 30 | 31 | class _drgn_token(ctypes.Structure): 32 | _fields_ = [ 33 | ("kind", ctypes.c_int), 34 | ("value", ctypes.c_void_p), 35 | ("len", ctypes.c_size_t), 36 | ] 37 | 38 | 39 | _drgn_cdll.drgn_test_serialize_bits.restype = None 40 | _drgn_cdll.drgn_test_serialize_bits.argtypes = [ 41 | ctypes.c_void_p, 42 | ctypes.c_uint64, 43 | ctypes.c_uint64, 44 | ctypes.c_uint8, 45 | ctypes.c_bool, 46 | ] 47 | _drgn_cdll.drgn_test_deserialize_bits.restype = ctypes.c_uint64 48 | _drgn_cdll.drgn_test_deserialize_bits.argtypes = [ 49 | ctypes.c_void_p, 50 | ctypes.c_uint64, 51 | ctypes.c_uint8, 52 | ctypes.c_bool, 53 | ] 54 | 55 | 56 | def serialize_bits(buf, bit_offset, uvalue, bit_size, little_endian): 57 | assert (bit_offset + bit_size + 7) // 8 <= len(buf) 58 | c_buf = (ctypes.c_char * len(buf)).from_buffer(buf) 59 | return _drgn_cdll.drgn_test_serialize_bits( 60 | c_buf, bit_offset, uvalue, bit_size, little_endian 61 | ) 62 | 63 | 64 | def deserialize_bits(buf, bit_offset, bit_size, little_endian): 65 | assert (bit_offset + bit_size + 7) // 8 <= len(buf) 66 | c_buf = (ctypes.c_char * len(buf)).from_buffer_copy(buf) 67 | return _drgn_cdll.drgn_test_deserialize_bits( 68 | c_buf, bit_offset, bit_size, little_endian 69 | ) 70 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/linux_kernel/helpers/__init__.py -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_block.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import os 5 | import os.path 6 | 7 | from drgn import Object 8 | from drgn.helpers.linux.block import ( 9 | bdev_partno, 10 | disk_devt, 11 | disk_name, 12 | for_each_disk, 13 | for_each_partition, 14 | part_devt, 15 | part_name, 16 | ) 17 | from drgn.helpers.linux.device import MAJOR, MINOR 18 | from tests.linux_kernel import LinuxKernelTestCase 19 | 20 | 21 | class TestBlock(LinuxKernelTestCase): 22 | def test_disk_devt(self): 23 | for disk in for_each_disk(self.prog): 24 | path = os.path.join(b"/sys/block", disk_name(disk), b"dev") 25 | with open(path, "r") as f: 26 | expected = f.read().strip() 27 | devt = disk_devt(disk).value_() 28 | self.assertEqual(f"{MAJOR(devt)}:{MINOR(devt)}", expected) 29 | 30 | def test_for_each_disk(self): 31 | self.assertEqual( 32 | {disk_name(disk).decode() for disk in for_each_disk(self.prog)}, 33 | set(os.listdir("/sys/block")), 34 | ) 35 | 36 | def test_part_devt(self): 37 | for part in for_each_partition(self.prog): 38 | path = os.path.join(b"/sys/class/block", part_name(part), b"dev") 39 | with open(path, "r") as f: 40 | expected = f.read().strip() 41 | devt = part_devt(part).value_() 42 | self.assertEqual(f"{MAJOR(devt)}:{MINOR(devt)}", expected) 43 | 44 | def test_for_each_partition(self): 45 | self.assertEqual( 46 | {part_name(part).decode() for part in for_each_partition(self.prog)}, 47 | set(os.listdir("/sys/class/block")), 48 | ) 49 | 50 | def test_bdev_partno(self): 51 | for part in for_each_partition(self.prog): 52 | try: 53 | with open( 54 | os.path.join(b"/sys/class/block", part_name(part), b"partition"), 55 | "r", 56 | ) as f: 57 | partition = int(f.read()) 58 | except FileNotFoundError: 59 | partition = 0 60 | if part.type_.type.tag == "hd_struct": 61 | self.skipTest("can't get bdev easily on old kernels") 62 | self.assertIdentical(bdev_partno(part), Object(self.prog, "u8", partition)) 63 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_boot.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import re 5 | import unittest 6 | 7 | from _drgn_util.platform import NORMALIZED_MACHINE_NAME 8 | from drgn.helpers.linux.boot import pgtable_l5_enabled 9 | from tests.linux_kernel import LinuxKernelTestCase 10 | 11 | 12 | class TestBoot(LinuxKernelTestCase): 13 | @unittest.skipUnless(NORMALIZED_MACHINE_NAME == "x86_64", "machine is not x86_64") 14 | def test_pgtable_l5_enabled(self): 15 | with open("/proc/cpuinfo", "r") as f: 16 | self.assertEqual( 17 | pgtable_l5_enabled(self.prog), 18 | bool(re.search(r"flags\s*:.*\bla57\b", f.read())), 19 | ) 20 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_cpumask.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from pathlib import Path 5 | 6 | import drgn.helpers.linux.cpumask 7 | from drgn.helpers.linux.cpumask import cpumask_to_cpulist 8 | from tests import classCleanups 9 | from tests.linux_kernel import LinuxKernelTestCase, parse_range_list 10 | 11 | CPU_PATH = Path("/sys/devices/system/cpu") 12 | 13 | 14 | class TestCpuMask(LinuxKernelTestCase): 15 | _MASKS = ("online", "possible", "present") 16 | 17 | @classmethod 18 | @classCleanups 19 | def setUpClass(cls): 20 | super().setUpClass() 21 | for online_path in sorted(CPU_PATH.glob("cpu*/online")): 22 | if int(online_path.read_text()): 23 | cls.offlined_path = online_path 24 | online_path.write_text("0") 25 | cls.addClassCleanup(online_path.write_text, "1") 26 | break 27 | 28 | def test_for_each_cpu(self): 29 | for name in self._MASKS: 30 | with self.subTest(name=name): 31 | self.assertEqual( 32 | list( 33 | getattr(drgn.helpers.linux.cpumask, f"for_each_{name}_cpu")( 34 | self.prog 35 | ) 36 | ), 37 | sorted(parse_range_list((CPU_PATH / name).read_text())), 38 | ) 39 | 40 | def test_cpumask_to_cpulist(self): 41 | for name in self._MASKS: 42 | with self.subTest(name=name): 43 | self.assertEqual( 44 | cpumask_to_cpulist( 45 | getattr(drgn.helpers.linux.cpumask, f"cpu_{name}_mask")( 46 | self.prog 47 | ) 48 | ), 49 | (CPU_PATH / name).read_text().strip(), 50 | ) 51 | 52 | def test_cpumask_weight(self): 53 | for name in self._MASKS: 54 | with self.subTest(name=name): 55 | self.assertEqual( 56 | getattr(drgn.helpers.linux.cpumask, f"num_{name}_cpus")(self.prog), 57 | len(parse_range_list((CPU_PATH / name).read_text())), 58 | ) 59 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_kconfig.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import gzip 5 | import re 6 | import unittest 7 | 8 | from _drgn_util.platform import NORMALIZED_MACHINE_NAME 9 | from drgn.helpers.linux.kconfig import get_kconfig 10 | from tests.linux_kernel import LinuxKernelTestCase 11 | 12 | 13 | class TestKconfig(LinuxKernelTestCase): 14 | @unittest.skipIf( 15 | NORMALIZED_MACHINE_NAME == "arm", 16 | "get_kconfig() is broken on Arm due to elfutils bug", 17 | ) 18 | def test_get_kconfig(self): 19 | expected = {} 20 | try: 21 | with gzip.open("/proc/config.gz", "rt") as f: 22 | for line in f: 23 | match = re.match(r"(\w+)=(.*)", line) 24 | if match: 25 | expected[match.group(1)] = match.group(2) 26 | except FileNotFoundError: 27 | self.skipTest("kernel not built with CONFIG_IKCONFIG_PROC") 28 | self.assertEqual(get_kconfig(self.prog), expected) 29 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_kthread.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from drgn import Object 5 | from drgn.helpers.linux.kthread import kthread_data 6 | from tests.linux_kernel import LinuxKernelTestCase, skip_unless_have_test_kmod 7 | 8 | 9 | @skip_unless_have_test_kmod 10 | class TestKthread(LinuxKernelTestCase): 11 | # There's no good way to test to_kthread() directly, but it gets tested 12 | # indirectly through kthread_data(). 13 | def test_kthread_data(self): 14 | self.assertIdentical( 15 | kthread_data(self.prog["drgn_test_kthread"]), 16 | Object(self.prog, "void *", 0xB0BA000), 17 | ) 18 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_module.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Oracle and/or its affiliates 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | from drgn.helpers.linux.module import ( 4 | address_to_module, 5 | find_module, 6 | for_each_module, 7 | module_address_regions, 8 | module_percpu_region, 9 | ) 10 | from tests.linux_kernel import LinuxKernelTestCase, skip_unless_have_test_kmod 11 | 12 | 13 | class TestListModules(LinuxKernelTestCase): 14 | def test_for_each_module(self): 15 | sys_modules = set(line.split(maxsplit=1)[0] for line in open("/proc/modules")) 16 | drgn_modules = set() 17 | for module in for_each_module(self.prog): 18 | drgn_modules.add(module.name.string_().decode()) 19 | 20 | self.assertEqual(sys_modules, drgn_modules) 21 | 22 | 23 | @skip_unless_have_test_kmod 24 | class TestModules(LinuxKernelTestCase): 25 | @classmethod 26 | def setUpClass(cls): 27 | super().setUpClass() 28 | cls.mod = find_module(cls.prog, "drgn_test") 29 | 30 | def test_module_percpu_region(self): 31 | pcpu_addr = self.prog.symbol("drgn_test_percpu_static").address 32 | start, size = module_percpu_region(self.mod) 33 | if start == 0: 34 | self.skipTest("No module percpu region on !SMP") 35 | self.assertTrue(start <= pcpu_addr <= start + size) 36 | 37 | def test_module_address_regions(self): 38 | regions = module_address_regions(self.mod) 39 | 40 | def assertInRegions(addr): 41 | for start, size in regions: 42 | if start <= addr < start + size: 43 | break 44 | else: 45 | self.fail(f"address {addr:x} not found in drgn_test module regions") 46 | 47 | self.assertEqual(address_to_module(self.prog, addr), self.mod) 48 | 49 | # function symbol (should be in .text) 50 | assertInRegions(self.prog.symbol("drgn_test_function").address) 51 | # variable symbol (should be in .data) 52 | assertInRegions(self.prog.symbol("drgn_test_empty_list").address) 53 | # constant variable (should be in .rodata) 54 | assertInRegions(self.prog.symbol("drgn_test_have_maple_tree").address) 55 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_nodemask.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) ByteDance, Inc. and its affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from pathlib import Path 5 | import unittest 6 | 7 | from drgn.helpers.linux.nodemask import for_each_node, for_each_online_node, node_state 8 | from tests.linux_kernel import LinuxKernelTestCase, parse_range_list 9 | 10 | NODE_PATH = Path("/sys/devices/system/node") 11 | 12 | 13 | @unittest.skipUnless(NODE_PATH.exists(), "kernel does not support NUMA") 14 | class TestNodeMask(LinuxKernelTestCase): 15 | @staticmethod 16 | def _parse_node_list(name): 17 | return parse_range_list((NODE_PATH / name).read_text()) 18 | 19 | def _test_for_each_node(self, func, name): 20 | self.assertEqual(list(func(self.prog)), sorted(self._parse_node_list(name))) 21 | 22 | def test_for_each_node(self): 23 | self._test_for_each_node(for_each_node, "possible") 24 | 25 | def test_for_each_online_node(self): 26 | self._test_for_each_node(for_each_online_node, "online") 27 | 28 | def _test_node_state(self, state_name, file_name): 29 | possible = self._parse_node_list("possible") 30 | expected = self._parse_node_list(file_name) 31 | state = self.prog[state_name] 32 | for node in possible: 33 | self.assertEqual(node_state(node, state), node in expected) 34 | 35 | def test_node_state(self): 36 | self._test_node_state("N_NORMAL_MEMORY", "has_normal_memory") 37 | # N_GENERIC_INITIATOR was added in Linux kernel commit 894c26a1c274 38 | # ("ACPI: Support Generic Initiator only domains") (in v5.10). Most of 39 | # the time it is unset, so if it exists we can use it to test the unset 40 | # case. 41 | if (NODE_PATH / "has_generic_initiator").exists(): 42 | self._test_node_state("N_GENERIC_INITIATOR", "has_generic_initiator") 43 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_percpu.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from drgn.helpers.linux.cpumask import for_each_possible_cpu 5 | from drgn.helpers.linux.percpu import per_cpu, per_cpu_ptr 6 | from tests.linux_kernel import ( 7 | LinuxKernelTestCase, 8 | prng32, 9 | skip_unless_have_test_kmod, 10 | smp_enabled, 11 | ) 12 | 13 | 14 | class TestPerCpu(LinuxKernelTestCase): 15 | def test_per_cpu(self): 16 | smp = smp_enabled() 17 | for cpu in for_each_possible_cpu(self.prog): 18 | if smp: 19 | self.assertEqual(per_cpu(self.prog["runqueues"], cpu).cpu, cpu) 20 | else: 21 | # struct rq::cpu only exists if CONFIG_SMP=y, so just check 22 | # that we get something valid. 23 | self.assertEqual( 24 | per_cpu(self.prog["runqueues"], cpu).idle.comm.string_(), b"swapper" 25 | ) 26 | 27 | @skip_unless_have_test_kmod 28 | def test_per_cpu_module_static(self): 29 | for cpu, expected in zip(for_each_possible_cpu(self.prog), prng32("PCPU")): 30 | self.assertEqual( 31 | per_cpu(self.prog["drgn_test_percpu_static"], cpu), expected 32 | ) 33 | 34 | @skip_unless_have_test_kmod 35 | def test_per_cpu_module_dynamic(self): 36 | for cpu, expected in zip(for_each_possible_cpu(self.prog), prng32("pcpu")): 37 | self.assertEqual( 38 | per_cpu_ptr(self.prog["drgn_test_percpu_dynamic"], cpu)[0], expected 39 | ) 40 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_pid.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from multiprocessing import Barrier, Process 5 | import os 6 | 7 | from drgn.helpers.linux.pid import find_pid, find_task, for_each_pid, for_each_task 8 | from tests.linux_kernel import LinuxKernelTestCase 9 | 10 | 11 | class TestPid(LinuxKernelTestCase): 12 | def test_find_pid(self): 13 | pid = os.getpid() 14 | self.assertEqual(find_pid(self.prog, pid).numbers[0].nr, pid) 15 | 16 | def test_for_each_pid(self): 17 | pid = os.getpid() 18 | self.assertTrue( 19 | any( 20 | pid_struct.numbers[0].nr == pid 21 | for pid_struct in for_each_pid(self.prog) 22 | ) 23 | ) 24 | 25 | def test_find_task(self): 26 | pid = os.getpid() 27 | with open("/proc/self/comm", "rb") as f: 28 | comm = f.read()[:-1] 29 | task = find_task(self.prog, os.getpid()) 30 | self.assertEqual(task.pid, pid) 31 | self.assertEqual(task.comm.string_(), comm) 32 | 33 | def test_for_each_task(self): 34 | NUM_PROCS = 12 35 | barrier = Barrier(NUM_PROCS + 1) 36 | 37 | def proc_func(): 38 | barrier.wait() 39 | 40 | try: 41 | procs = [Process(target=proc_func) for _ in range(NUM_PROCS)] 42 | for proc in procs: 43 | proc.start() 44 | pids = {task.pid.value_() for task in for_each_task(self.prog)} 45 | for proc in procs: 46 | self.assertIn(proc.pid, pids) 47 | self.assertIn(os.getpid(), pids) 48 | barrier.wait() 49 | except BaseException: 50 | barrier.abort() 51 | for proc in procs: 52 | proc.terminate() 53 | raise 54 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_stackdepot.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import unittest 5 | 6 | from drgn.helpers.linux.stackdepot import stack_depot_fetch 7 | from tests.linux_kernel import skip_unless_have_test_kmod 8 | from tests.linux_kernel.test_stack_trace import LinuxKernelStackTraceTestCase 9 | 10 | 11 | @skip_unless_have_test_kmod 12 | class TestStackDepot(LinuxKernelStackTraceTestCase): 13 | @classmethod 14 | def setUpClass(cls): 15 | super().setUpClass() 16 | if not cls.prog["drgn_test_have_stackdepot"]: 17 | raise unittest.SkipTest("kernel does not have stack depot") 18 | 19 | @skip_unless_have_test_kmod 20 | def test_stack_depot_fetch(self): 21 | self._test_drgn_test_kthread_trace( 22 | stack_depot_fetch(self.prog["drgn_test_stack_handle"]) 23 | ) 24 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_tcp.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import os 5 | import socket 6 | 7 | from drgn import cast 8 | from drgn.helpers.linux.fs import fget 9 | from drgn.helpers.linux.pid import find_task 10 | from drgn.helpers.linux.tcp import sk_tcpstate 11 | from tests.linux_kernel import LinuxKernelTestCase, create_socket 12 | 13 | 14 | class TestTcp(LinuxKernelTestCase): 15 | def test_sk_tcpstate(self): 16 | with create_socket() as sock: 17 | task = find_task(self.prog, os.getpid()) 18 | file = fget(task, sock.fileno()) 19 | sk = cast("struct socket *", file.private_data).sk 20 | self.assertEqual(sk_tcpstate(sk), self.prog["TCP_CLOSE"]) 21 | 22 | sock.bind(("localhost", 0)) 23 | sock.listen() 24 | self.assertEqual(sk_tcpstate(sk), self.prog["TCP_LISTEN"]) 25 | 26 | with socket.create_connection(sock.getsockname()), sock.accept()[ 27 | 0 28 | ] as sock2: 29 | file = fget(task, sock2.fileno()) 30 | sk = cast("struct socket *", file.private_data).sk 31 | self.assertEqual(sk_tcpstate(sk), self.prog["TCP_ESTABLISHED"]) 32 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_user.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import contextlib 5 | import os 6 | 7 | from drgn.helpers.linux.user import find_user, for_each_user 8 | from tests.linux_kernel import LinuxKernelTestCase, fork_and_stop 9 | 10 | 11 | class TestUser(LinuxKernelTestCase): 12 | # Try a few UIDs in case the the hash function changes in the future. 13 | UIDS = frozenset({0, 430, 1000, 65537}) 14 | 15 | def test_find_user(self): 16 | for uid in self.UIDS: 17 | with fork_and_stop(os.setuid, uid): 18 | found_uid = find_user(self.prog, uid).uid.val.value_() 19 | self.assertEqual(found_uid, uid) 20 | 21 | def test_for_each_user(self): 22 | with contextlib.ExitStack() as stack: 23 | for uid in self.UIDS: 24 | stack.enter_context(fork_and_stop(os.setuid, uid)) 25 | found_uids = {user.uid.val.value_() for user in for_each_user(self.prog)} 26 | self.assertTrue(self.UIDS.issubset(found_uids)) 27 | -------------------------------------------------------------------------------- /tests/linux_kernel/helpers/test_wait.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Oracle and/or its affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from drgn.helpers.linux.wait import waitqueue_active, waitqueue_for_each_task 5 | from tests.linux_kernel import LinuxKernelTestCase, skip_unless_have_test_kmod 6 | 7 | 8 | @skip_unless_have_test_kmod 9 | class TestWaitqueue(LinuxKernelTestCase): 10 | @classmethod 11 | def setUpClass(cls): 12 | super().setUpClass() 13 | cls.waitq = cls.prog["drgn_test_waitq"].address_of_() 14 | cls.empty_waitq = cls.prog["drgn_test_empty_waitq"].address_of_() 15 | 16 | def test_waitqueue_active(self): 17 | self.assertTrue(waitqueue_active(self.waitq)) 18 | self.assertFalse(waitqueue_active(self.empty_waitq)) 19 | 20 | def test_waitqueue_for_each_task(self): 21 | self.assertEqual(list(waitqueue_for_each_task(self.empty_waitq)), []) 22 | self.assertEqual( 23 | list(waitqueue_for_each_task(self.waitq)), 24 | [self.prog["drgn_test_waitq_kthread"]], 25 | ) 26 | -------------------------------------------------------------------------------- /tests/linux_kernel/kmod/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := drgn_test.o 2 | -------------------------------------------------------------------------------- /tests/linux_kernel/test_special_objects.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import os 5 | 6 | from drgn import Object, Program 7 | from tests.linux_kernel import LinuxKernelTestCase 8 | 9 | 10 | class TestJiffies(LinuxKernelTestCase): 11 | def test_jiffies(self): 12 | self.assertIdentical( 13 | self.prog["jiffies"], 14 | Object( 15 | self.prog, 16 | "volatile unsigned long", 17 | address=self.prog.symbol("jiffies").address, 18 | ), 19 | ) 20 | 21 | 22 | class TestUts(LinuxKernelTestCase): 23 | def test_uts_release(self): 24 | self.assertEqual( 25 | self.prog["UTS_RELEASE"].string_().decode(), os.uname().release 26 | ) 27 | 28 | def test_uts_release_no_debug_info(self): 29 | prog = Program() 30 | prog.set_kernel() 31 | self.assertEqual(prog["UTS_RELEASE"].string_().decode(), os.uname().release) 32 | 33 | 34 | class TestVmcoreinfo(LinuxKernelTestCase): 35 | def test_vmcoreinfo(self): 36 | vmcoreinfo_data = dict( 37 | line.split("=", 1) 38 | for line in self.prog["VMCOREINFO"].string_().decode().strip().split("\n") 39 | ) 40 | self.assertEqual( 41 | int(vmcoreinfo_data["SYMBOL(init_uts_ns)"], 16), 42 | self.prog.symbol("init_uts_ns").address, 43 | ) 44 | 45 | def test_vmcoreinfo_no_debug_info(self): 46 | prog = Program() 47 | prog.set_kernel() 48 | vmcoreinfo_data = dict( 49 | line.split("=", 1) 50 | for line in prog["VMCOREINFO"].string_().decode().strip().split("\n") 51 | ) 52 | self.assertEqual( 53 | vmcoreinfo_data["OSRELEASE"], 54 | os.uname().release, 55 | ) 56 | -------------------------------------------------------------------------------- /tests/linux_kernel/test_symbol.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, Oracle and/or its affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from drgn import SymbolBinding, SymbolKind 5 | from tests.linux_kernel import LinuxKernelTestCase, skip_unless_have_test_kmod 6 | 7 | 8 | class TestSymbol(LinuxKernelTestCase): 9 | def test_global_symbol(self): 10 | symbol = self.prog.symbol("jiffies") 11 | self.assertEqual(symbol.name, "jiffies") 12 | self.assertEqual(symbol.binding, SymbolBinding.GLOBAL) 13 | self.assertEqual(symbol.kind, SymbolKind.OBJECT) 14 | 15 | @skip_unless_have_test_kmod 16 | def test_module_function_symbol(self): 17 | symbol = self.prog.symbol("drgn_test_function") 18 | self.assertEqual(symbol.name, "drgn_test_function") 19 | self.assertEqual(symbol.binding, SymbolBinding.GLOBAL) 20 | self.assertEqual(symbol.kind, SymbolKind.FUNC) 21 | 22 | symbol = self.prog.symbol(symbol.address) 23 | self.assertEqual(symbol.name, "drgn_test_function") 24 | self.assertEqual(symbol.binding, SymbolBinding.GLOBAL) 25 | self.assertEqual(symbol.kind, SymbolKind.FUNC) 26 | 27 | @skip_unless_have_test_kmod 28 | def test_module_data_symbol(self): 29 | symbol = self.prog.symbol("drgn_test_data") 30 | self.assertEqual(symbol.name, "drgn_test_data") 31 | self.assertEqual(symbol.binding, SymbolBinding.GLOBAL) 32 | self.assertEqual(symbol.kind, SymbolKind.OBJECT) 33 | 34 | symbol = self.prog.symbol(symbol.address) 35 | self.assertEqual(symbol.name, "drgn_test_data") 36 | self.assertEqual(symbol.binding, SymbolBinding.GLOBAL) 37 | self.assertEqual(symbol.kind, SymbolKind.OBJECT) 38 | 39 | @skip_unless_have_test_kmod 40 | def test_module_rodata_symbol(self): 41 | symbol = self.prog.symbol("drgn_test_rodata") 42 | self.assertEqual(symbol.name, "drgn_test_rodata") 43 | self.assertEqual(symbol.binding, SymbolBinding.GLOBAL) 44 | self.assertEqual(symbol.kind, SymbolKind.OBJECT) 45 | 46 | symbol = self.prog.symbol(symbol.address) 47 | self.assertEqual(symbol.name, "drgn_test_rodata") 48 | self.assertEqual(symbol.binding, SymbolBinding.GLOBAL) 49 | self.assertEqual(symbol.kind, SymbolKind.OBJECT) 50 | -------------------------------------------------------------------------------- /tests/linux_kernel/test_threads.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from multiprocessing import Barrier, Process 5 | import os 6 | 7 | from drgn.helpers.linux.pid import find_task 8 | from tests.linux_kernel import LinuxKernelTestCase 9 | 10 | 11 | class TestThreads(LinuxKernelTestCase): 12 | def test_threads(self): 13 | NUM_PROCS = 12 14 | barrier = Barrier(NUM_PROCS + 1) 15 | 16 | def proc_func(): 17 | barrier.wait() 18 | 19 | try: 20 | procs = [Process(target=proc_func) for _ in range(NUM_PROCS)] 21 | for proc in procs: 22 | proc.start() 23 | pids = {thread.tid for thread in self.prog.threads()} 24 | for proc in procs: 25 | self.assertIn(proc.pid, pids) 26 | self.assertIn(os.getpid(), pids) 27 | barrier.wait() 28 | except BaseException: 29 | barrier.abort() 30 | for proc in procs: 31 | proc.terminate() 32 | raise 33 | 34 | def test_thread(self): 35 | pid = os.getpid() 36 | thread = self.prog.thread(pid) 37 | self.assertEqual(thread.tid, pid) 38 | self.assertEqual(thread.object, find_task(self.prog, pid)) 39 | 40 | def test_main_thread(self): 41 | self.assertRaisesRegex( 42 | ValueError, 43 | "main thread is not defined for the Linux kernel", 44 | self.prog.main_thread, 45 | ) 46 | 47 | def test_crashed_thread(self): 48 | self.assertRaisesRegex( 49 | ValueError, 50 | "crashed thread is only defined for core dumps", 51 | self.prog.crashed_thread, 52 | ) 53 | 54 | def test_thread_name(self): 55 | pid = os.getpid() 56 | with open(f"/proc/{pid}/comm", "r") as f: 57 | comm = f.read().strip() 58 | thread = self.prog.thread(pid) 59 | self.assertEqual(comm, thread.name) 60 | -------------------------------------------------------------------------------- /tests/linux_kernel/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/linux_kernel/tools/__init__.py -------------------------------------------------------------------------------- /tests/linux_kernel/vmcore/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from pathlib import Path 5 | import unittest 6 | 7 | import drgn 8 | from tests import TestCase 9 | from tests.linux_kernel import LinuxKernelTestCase 10 | 11 | VMCORE_PATH = Path("/proc/vmcore") 12 | 13 | 14 | @unittest.skipUnless(VMCORE_PATH.exists(), "not running in kdump") 15 | class LinuxVMCoreTestCase(TestCase): 16 | prog = None 17 | 18 | @classmethod 19 | def setUpClass(cls): 20 | super().setUpClass() 21 | # We only want to create the Program once for all tests, so it's cached 22 | # as a class variable (in the base class). 23 | if LinuxVMCoreTestCase.prog is None: 24 | prog = drgn.Program() 25 | prog.set_core_dump(VMCORE_PATH) 26 | LinuxKernelTestCase._load_debug_info(prog) 27 | LinuxVMCoreTestCase.prog = prog 28 | -------------------------------------------------------------------------------- /tests/resources/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !/.gitignore 3 | !/__init__.py 4 | !/__main__.py 5 | !*.zst 6 | -------------------------------------------------------------------------------- /tests/resources/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import os 5 | from pathlib import Path 6 | import subprocess 7 | import tempfile 8 | import unittest 9 | 10 | from util import out_of_date 11 | 12 | 13 | def get_resource(name: str) -> Path: 14 | dir = Path(__file__).parent 15 | decompressed_path = dir / name 16 | compressed_path = dir / (name + ".zst") 17 | if out_of_date(decompressed_path, compressed_path): 18 | tmp_file = tempfile.NamedTemporaryFile(dir=dir, prefix=name, delete=False) 19 | try: 20 | try: 21 | subprocess.check_call( 22 | [ 23 | "zstd", 24 | "--quiet", 25 | "--force", 26 | "--decompress", 27 | "--stdout", 28 | str(compressed_path), 29 | ], 30 | stdout=tmp_file, 31 | ) 32 | except FileNotFoundError: 33 | raise unittest.SkipTest("zstd not found") 34 | except BaseException: 35 | os.unlink(tmp_file.name) 36 | raise 37 | else: 38 | os.rename(tmp_file.name, decompressed_path) 39 | return decompressed_path 40 | -------------------------------------------------------------------------------- /tests/resources/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import argparse 5 | 6 | from tests.resources import get_resource 7 | 8 | parser = argparse.ArgumentParser( 9 | description="decompress test resources and print their paths" 10 | ) 11 | parser.add_argument("name", nargs="+", help="resource name") 12 | args = parser.parse_args() 13 | 14 | for name in args.name: 15 | print(get_resource(name)) 16 | -------------------------------------------------------------------------------- /tests/resources/crashme.alt.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme.alt.zst -------------------------------------------------------------------------------- /tests/resources/crashme.core.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme.core.zst -------------------------------------------------------------------------------- /tests/resources/crashme.dwz.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme.dwz.zst -------------------------------------------------------------------------------- /tests/resources/crashme.so.dwz.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme.so.dwz.zst -------------------------------------------------------------------------------- /tests/resources/crashme.so.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme.so.zst -------------------------------------------------------------------------------- /tests/resources/crashme.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme.zst -------------------------------------------------------------------------------- /tests/resources/crashme_pie.core.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme_pie.core.zst -------------------------------------------------------------------------------- /tests/resources/crashme_pie.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme_pie.zst -------------------------------------------------------------------------------- /tests/resources/crashme_pie_no_headers.core.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme_pie_no_headers.core.zst -------------------------------------------------------------------------------- /tests/resources/crashme_static.core.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme_static.core.zst -------------------------------------------------------------------------------- /tests/resources/crashme_static.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme_static.zst -------------------------------------------------------------------------------- /tests/resources/crashme_static_pie.core.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme_static_pie.core.zst -------------------------------------------------------------------------------- /tests/resources/crashme_static_pie.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/crashme_static_pie.zst -------------------------------------------------------------------------------- /tests/resources/multithreaded.core.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tests/resources/multithreaded.core.zst -------------------------------------------------------------------------------- /tests/test_docs.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | import pydoc 4 | 5 | import drgn 6 | from tests import TestCase 7 | 8 | 9 | class TestDocs(TestCase): 10 | def test_render(self): 11 | pydoc.render_doc(drgn) 12 | -------------------------------------------------------------------------------- /tests/test_filename_matches.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from drgn import filename_matches 5 | from tests import TestCase 6 | 7 | 8 | class TestFilenameMatches(TestCase): 9 | def test_filename_matches(self): 10 | self.assertTrue(filename_matches("ab/cd/ef", "ef")) 11 | self.assertTrue(filename_matches("ab/cd/ef", "cd/ef")) 12 | self.assertFalse(filename_matches("ab/cd/ef", "d/ef")) 13 | self.assertFalse(filename_matches("/ef", "cd/ef")) 14 | self.assertTrue(filename_matches("/abc", "abc")) 15 | self.assertFalse(filename_matches("abc", "/abc")) 16 | 17 | def test_empty(self): 18 | self.assertTrue(filename_matches("", "")) 19 | self.assertTrue(filename_matches("ab", "")) 20 | self.assertFalse(filename_matches("", "ab")) 21 | 22 | def test_one_component(self): 23 | self.assertTrue(filename_matches("ab", "ab")) 24 | self.assertFalse(filename_matches("ab", "cd")) 25 | 26 | def test_multiple_components(self): 27 | self.assertTrue(filename_matches("ab/cd/ef", "ef")) 28 | self.assertTrue(filename_matches("ab/cd/ef", "cd/ef")) 29 | self.assertFalse(filename_matches("ab/cd/ef", "cd")) 30 | self.assertFalse(filename_matches("ab/cd/ef", "ab/ef")) 31 | self.assertFalse(filename_matches("ef", "ab/cd/ef")) 32 | 33 | def test_component_substring(self): 34 | self.assertFalse(filename_matches("ab/cd/ef", "d/ef")) 35 | 36 | def test_absolute(self): 37 | self.assertTrue(filename_matches("/abc", "abc")) 38 | self.assertFalse(filename_matches("abc", "/abc")) 39 | -------------------------------------------------------------------------------- /tests/test_logging.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import logging 5 | import sys 6 | import unittest 7 | 8 | from drgn import Program 9 | from tests import TestCase 10 | 11 | 12 | # Test that our monkey patch to sync the log level between the logging module 13 | # and libdrgn works. 14 | class TestLogging(TestCase): 15 | def test_set_level_before(self): 16 | logger = logging.getLogger("drgn") 17 | with self.assertLogs(logger, "DEBUG") as cm: 18 | prog = Program() 19 | prog._log(0, "foo") 20 | self.assertIn("DEBUG:drgn:foo", cm.output) 21 | 22 | @unittest.skipIf( 23 | sys.version_info < (3, 7), "syncing log level only works since Python 3.7" 24 | ) 25 | def test_set_level_after(self): 26 | prog = Program() 27 | logger = logging.getLogger("drgn") 28 | with self.assertLogs(logger, "DEBUG") as cm: 29 | prog._log(0, "bar") 30 | self.assertIn("DEBUG:drgn:bar", cm.output) 31 | -------------------------------------------------------------------------------- /tests/test_platform.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | import itertools 4 | 5 | from drgn import Architecture, Platform, PlatformFlags 6 | from tests import TestCase 7 | 8 | 9 | class TestPlatform(TestCase): 10 | def test_default_flags(self): 11 | Platform(Architecture.X86_64) 12 | self.assertRaises(ValueError, Platform, Architecture.UNKNOWN) 13 | 14 | def test_registers(self): 15 | self.assertIn( 16 | "rax", 17 | itertools.chain.from_iterable( 18 | reg.names for reg in Platform(Architecture.X86_64).registers 19 | ), 20 | ) 21 | self.assertEqual(Platform(Architecture.UNKNOWN, PlatformFlags(0)).registers, ()) 22 | -------------------------------------------------------------------------------- /tests/test_python.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | import _drgn 4 | import drgn 5 | from tests import TestCase 6 | 7 | 8 | class TestModule(TestCase): 9 | def test_all(self): 10 | # At least for now, everything in the Python library should go in 11 | # __all__, so make sure that happens. 12 | from_python = { 13 | name 14 | for name in dir(drgn) 15 | if not name.startswith("_") 16 | and getattr(getattr(drgn, name), "__module__", "").startswith("drgn") 17 | } 18 | self.assertEqual(from_python - set(drgn.__all__), set()) 19 | 20 | def test_bindings(self): 21 | # Make sure everything in the C extension (_drgn) is added to the 22 | # Python library (drgn). 23 | from_extension = {name for name in dir(_drgn) if not name.startswith("_")} 24 | self.assertEqual(from_extension - set(dir(drgn)), set()) 25 | self.assertEqual(from_extension - set(drgn.__all__), set()) 26 | -------------------------------------------------------------------------------- /tests/test_stack_trace.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | from drgn import Program 5 | from tests import TestCase 6 | from tests.resources import get_resource 7 | 8 | 9 | class TestLinuxUserspaceCoreDump(TestCase): 10 | @classmethod 11 | def setUpClass(cls): 12 | cls.prog = Program() 13 | cls.prog.set_enabled_debug_info_finders([]) 14 | cls.prog.set_core_dump(get_resource("crashme.core")) 15 | cls.prog.load_debug_info([get_resource("crashme"), get_resource("crashme.so")]) 16 | cls.trace = cls.prog.crashed_thread().stack_trace() 17 | 18 | @classmethod 19 | def tearDownClass(cls): 20 | del cls.trace 21 | del cls.prog 22 | 23 | def test_stack_frame_name(self): 24 | self.assertEqual(self.trace[0].name, "c") 25 | self.assertEqual(self.trace[5].name, "0x7f6112ad8088") 26 | self.assertEqual(self.trace[7].name, "_start") 27 | self.assertEqual(self.trace[8].name, "???") 28 | 29 | def test_stack_frame_function_name(self): 30 | self.assertEqual(self.trace[0].function_name, "c") 31 | self.assertIsNone(self.trace[5].function_name) 32 | self.assertIsNone(self.trace[7].function_name) 33 | self.assertIsNone(self.trace[8].function_name) 34 | -------------------------------------------------------------------------------- /tests/test_util.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | from functools import cmp_to_key 4 | 5 | from tests import TestCase 6 | from util import KernelVersion, verrevcmp 7 | 8 | 9 | class TestUtil(TestCase): 10 | def assertVersionSort(self, sorted_list): 11 | self.assertEqual(sorted(sorted_list, key=cmp_to_key(verrevcmp)), sorted_list) 12 | 13 | def test_verrevcmp(self): 14 | self.assertVersionSort( 15 | ["0~", "0", "1", "1.0", "1.1~rc1", "1.1~rc2", "1.1", "1.2", "1.12"] 16 | ) 17 | self.assertVersionSort(["a", "."]) 18 | self.assertVersionSort(["", "1"]) 19 | self.assertVersionSort(["~", "~1"]) 20 | self.assertVersionSort(["~~", "~~a", "~", "", "a"]) 21 | 22 | def test_kernel_version(self): 23 | self.assertLess(KernelVersion("1.0"), KernelVersion("2.0")) 24 | self.assertLess(KernelVersion("5.6.0-rc6"), KernelVersion("5.6.0-rc7")) 25 | self.assertLess(KernelVersion("5.6.0-rc7"), KernelVersion("5.6.0")) 26 | self.assertLess( 27 | KernelVersion("5.6.0-rc7-vmtest2"), KernelVersion("5.6.0-vmtest1") 28 | ) 29 | self.assertLess(KernelVersion("5.6.0-vmtest1"), KernelVersion("5.6.0-vmtest2")) 30 | -------------------------------------------------------------------------------- /tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/tools/__init__.py -------------------------------------------------------------------------------- /vmtest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osandov/drgn/d5a101c7cf6bcbce55907ebc369a45aad433cbf9/vmtest/__init__.py -------------------------------------------------------------------------------- /vmtest/asynciosubprocess.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # SPDX-License-Identifier: LGPL-2.1-or-later 3 | 4 | import asyncio 5 | from contextlib import contextmanager 6 | import os 7 | from subprocess import CalledProcessError as CalledProcessError 8 | from typing import Any, Iterator, Tuple 9 | 10 | 11 | async def check_call(*args: Any, **kwds: Any) -> None: 12 | proc = await asyncio.create_subprocess_exec(*args, **kwds) 13 | returncode = await proc.wait() 14 | if returncode != 0: 15 | raise CalledProcessError(returncode, args) 16 | 17 | 18 | async def check_output(*args: Any, **kwds: Any) -> bytes: 19 | kwds["stdout"] = asyncio.subprocess.PIPE 20 | proc = await asyncio.create_subprocess_exec(*args, **kwds) 21 | stdout = (await proc.communicate())[0] 22 | if proc.returncode: 23 | raise CalledProcessError(proc.returncode, args) 24 | return stdout 25 | 26 | 27 | async def check_output_shell(cmd: str, **kwds: Any) -> bytes: 28 | kwds["stdout"] = asyncio.subprocess.PIPE 29 | proc = await asyncio.create_subprocess_shell(cmd, **kwds) 30 | stdout = (await proc.communicate())[0] 31 | if proc.returncode: 32 | raise CalledProcessError(proc.returncode, cmd) 33 | return stdout 34 | 35 | 36 | @contextmanager 37 | def pipe_context() -> Iterator[Tuple[int, int]]: 38 | pipe_r = pipe_w = None 39 | try: 40 | pipe_r, pipe_w = os.pipe() 41 | yield pipe_r, pipe_w 42 | finally: 43 | if pipe_r is not None: 44 | os.close(pipe_r) 45 | if pipe_w is not None: 46 | os.close(pipe_w) 47 | -------------------------------------------------------------------------------- /vmtest/patches/4.9-arm64-build-Remove-.eh_frame-sections-due-to-unwind-.patch: -------------------------------------------------------------------------------- 1 | From a099169787363c34a61420d67061ff7a354e5de3 Mon Sep 17 00:00:00 2001 2 | Message-ID: 3 | From: Kees Cook 4 | Date: Fri, 21 Aug 2020 12:42:51 -0700 5 | Subject: [PATCH] arm64/build: Remove .eh_frame* sections due to unwind tables 6 | 7 | Avoid .eh_frame* section generation by making sure both CFLAGS and AFLAGS 8 | contain -fno-asychronous-unwind-tables and -fno-unwind-tables. 9 | 10 | With all sources of .eh_frame now removed from the build, drop this 11 | DISCARD so we can be alerted in the future if it returns unexpectedly 12 | once orphan section warnings have been enabled. 13 | 14 | Suggested-by: Ard Biesheuvel 15 | Signed-off-by: Kees Cook 16 | Signed-off-by: Ingo Molnar 17 | Acked-by: Will Deacon 18 | Link: https://lore.kernel.org/r/20200821194310.3089815-11-keescook@chromium.org 19 | (cherry picked from commit 6e0a66d10c5b629369afa47b753d0ec46fa812dd) 20 | Signed-off-by: Omar Sandoval 21 | --- 22 | arch/arm64/Makefile | 5 ++++- 23 | 1 file changed, 4 insertions(+), 1 deletion(-) 24 | 25 | diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile 26 | index 8d469aa5fc98..d3fa0465765d 100644 27 | --- a/arch/arm64/Makefile 28 | +++ b/arch/arm64/Makefile 29 | @@ -42,10 +42,13 @@ $(warning LSE atomics not supported by binutils) 30 | endif 31 | 32 | KBUILD_CFLAGS += -mgeneral-regs-only $(lseinstr) 33 | -KBUILD_CFLAGS += -fno-asynchronous-unwind-tables 34 | KBUILD_CFLAGS += $(call cc-option, -mpc-relative-literal-loads) 35 | KBUILD_AFLAGS += $(lseinstr) 36 | 37 | +# Avoid generating .eh_frame* sections. 38 | +KBUILD_CFLAGS += -fno-asynchronous-unwind-tables -fno-unwind-tables 39 | +KBUILD_AFLAGS += -fno-asynchronous-unwind-tables -fno-unwind-tables 40 | + 41 | ifeq ($(CONFIG_CPU_BIG_ENDIAN), y) 42 | KBUILD_CPPFLAGS += -mbig-endian 43 | AS += -EB 44 | -- 45 | 2.44.0 46 | 47 | -------------------------------------------------------------------------------- /vmtest/patches/5.10-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch: -------------------------------------------------------------------------------- 1 | From bbaea0f1cd33d702d053d5bdaf6d6dec3932894c Mon Sep 17 00:00:00 2001 2 | Message-Id: 3 | From: Ilya Leoshkevich 4 | Date: Wed, 19 Oct 2022 10:56:00 +0200 5 | Subject: [PATCH] bpf: Generate BTF_KIND_FLOAT when linking vmlinux 6 | 7 | commit db16c1fe92d7ba7d39061faef897842baee2c887 upstream. 8 | 9 | [backported for dependency only extra_paholeopt variable setup and 10 | usage, we don't want floats generated in 5.10] 11 | 12 | pahole v1.21 supports the --btf_gen_floats flag, which makes it 13 | generate the information about the floating-point types [1]. 14 | 15 | Adjust link-vmlinux.sh to pass this flag to pahole in case it's 16 | supported, which is determined using a simple version check. 17 | 18 | [1] https://lore.kernel.org/dwarves/YHRiXNX1JUF2Az0A@kernel.org/ 19 | 20 | Signed-off-by: Ilya Leoshkevich 21 | Signed-off-by: Andrii Nakryiko 22 | Acked-by: Andrii Nakryiko 23 | Link: https://lore.kernel.org/bpf/20210413190043.21918-1-iii@linux.ibm.com 24 | Signed-off-by: Jiri Olsa 25 | Signed-off-by: Greg Kroah-Hartman 26 | --- 27 | scripts/link-vmlinux.sh | 3 ++- 28 | 1 file changed, 2 insertions(+), 1 deletion(-) 29 | 30 | diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh 31 | index d0b44bee9286..cdfccbfed452 100755 32 | --- a/scripts/link-vmlinux.sh 33 | +++ b/scripts/link-vmlinux.sh 34 | @@ -146,6 +146,7 @@ vmlinux_link() 35 | gen_btf() 36 | { 37 | local pahole_ver 38 | + local extra_paholeopt= 39 | 40 | if ! [ -x "$(command -v ${PAHOLE})" ]; then 41 | echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" 42 | @@ -161,7 +162,7 @@ gen_btf() 43 | vmlinux_link ${1} 44 | 45 | info "BTF" ${2} 46 | - LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${1} 47 | + LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${extra_paholeopt} ${1} 48 | 49 | # Create ${2} which contains just .BTF section but no symbols. Add 50 | # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all 51 | -- 52 | 2.30.2 53 | 54 | -------------------------------------------------------------------------------- /vmtest/patches/5.10-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch: -------------------------------------------------------------------------------- 1 | From ecad3312111798d84dac1ce6a853e0ac9de8d505 Mon Sep 17 00:00:00 2001 2 | Message-Id: 3 | From: Martin Rodriguez Reboredo 4 | Date: Wed, 19 Oct 2022 10:56:04 +0200 5 | Subject: [PATCH] kbuild: Add skip_encoding_btf_enum64 option to pahole 6 | 7 | New pahole (version 1.24) generates by default new BTF_KIND_ENUM64 BTF tag, 8 | which is not supported by stable kernel. 9 | 10 | As a result the kernel with CONFIG_DEBUG_INFO_BTF option will fail to 11 | compile with following error: 12 | 13 | BTFIDS vmlinux 14 | FAILED: load BTF from vmlinux: Invalid argument 15 | 16 | New pahole provides --skip_encoding_btf_enum64 option to skip BTF_KIND_ENUM64 17 | generation and produce BTF supported by stable kernel. 18 | 19 | Adding this option to scripts/pahole-flags.sh. 20 | 21 | This change does not have equivalent commit in linus tree, because linus tree 22 | has support for BTF_KIND_ENUM64 tag, so it does not need to be disabled. 23 | 24 | Signed-off-by: Martin Rodriguez Reboredo 25 | Signed-off-by: Jiri Olsa 26 | Signed-off-by: Greg Kroah-Hartman 27 | --- 28 | scripts/pahole-flags.sh | 4 ++++ 29 | 1 file changed, 4 insertions(+) 30 | 31 | diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh 32 | index 27445cb72974..8c82173e42e5 100755 33 | --- a/scripts/pahole-flags.sh 34 | +++ b/scripts/pahole-flags.sh 35 | @@ -14,4 +14,8 @@ if [ "${pahole_ver}" -ge "118" ] && [ "${pahole_ver}" -le "121" ]; then 36 | extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_vars" 37 | fi 38 | 39 | +if [ "${pahole_ver}" -ge "124" ]; then 40 | + extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_enum64" 41 | +fi 42 | + 43 | echo ${extra_paholeopt} 44 | -- 45 | 2.30.2 46 | 47 | -------------------------------------------------------------------------------- /vmtest/patches/5.11-bpf-Generate-BTF_KIND_FLOAT-when-linking-vmlinux.patch: -------------------------------------------------------------------------------- 1 | From 796deb4725c9b501305fb87002f22bdc851d28ab Mon Sep 17 00:00:00 2001 2 | Message-Id: <796deb4725c9b501305fb87002f22bdc851d28ab.1685662812.git.osandov@osandov.com> 3 | From: Ilya Leoshkevich 4 | Date: Tue, 13 Apr 2021 21:00:43 +0200 5 | Subject: [PATCH] bpf: Generate BTF_KIND_FLOAT when linking vmlinux 6 | 7 | pahole v1.21 supports the --btf_gen_floats flag, which makes it 8 | generate the information about the floating-point types [1]. 9 | 10 | Adjust link-vmlinux.sh to pass this flag to pahole in case it's 11 | supported, which is determined using a simple version check. 12 | 13 | [1] https://lore.kernel.org/dwarves/YHRiXNX1JUF2Az0A@kernel.org/ 14 | 15 | Signed-off-by: Ilya Leoshkevich 16 | Signed-off-by: Andrii Nakryiko 17 | Acked-by: Andrii Nakryiko 18 | Link: https://lore.kernel.org/bpf/20210413190043.21918-1-iii@linux.ibm.com 19 | (cherry picked from commit db16c1fe92d7ba7d39061faef897842baee2c887) 20 | [Omar: only define and use extra_paholeopt] 21 | Signed-off-by: Omar Sandoval 22 | --- 23 | scripts/link-vmlinux.sh | 3 ++- 24 | 1 file changed, 2 insertions(+), 1 deletion(-) 25 | 26 | diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh 27 | index 1ddb8961fc49..94c548b11db9 100755 28 | --- a/scripts/link-vmlinux.sh 29 | +++ b/scripts/link-vmlinux.sh 30 | @@ -140,6 +140,7 @@ vmlinux_link() 31 | gen_btf() 32 | { 33 | local pahole_ver 34 | + local extra_paholeopt= 35 | 36 | if ! [ -x "$(command -v ${PAHOLE})" ]; then 37 | echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" 38 | @@ -155,7 +156,7 @@ gen_btf() 39 | vmlinux_link ${1} 40 | 41 | info "BTF" ${2} 42 | - LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${1} 43 | + LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${extra_paholeopt} ${1} 44 | 45 | # Create ${2} which contains just .BTF section but no symbols. Add 46 | # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all 47 | -- 48 | 2.40.1 49 | 50 | -------------------------------------------------------------------------------- /vmtest/patches/5.15-kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch: -------------------------------------------------------------------------------- 1 | From b775fbf532dc01ae53a6fc56168fd30cb4b0c658 Mon Sep 17 00:00:00 2001 2 | Message-Id: 3 | From: Martin Rodriguez Reboredo 4 | Date: Sun, 4 Sep 2022 15:19:01 +0200 5 | Subject: [PATCH] kbuild: Add skip_encoding_btf_enum64 option to pahole 6 | 7 | New pahole (version 1.24) generates by default new BTF_KIND_ENUM64 BTF tag, 8 | which is not supported by stable kernel. 9 | 10 | As a result the kernel with CONFIG_DEBUG_INFO_BTF option will fail to 11 | compile with following error: 12 | 13 | BTFIDS vmlinux 14 | FAILED: load BTF from vmlinux: Invalid argument 15 | 16 | New pahole provides --skip_encoding_btf_enum64 option to skip BTF_KIND_ENUM64 17 | generation and produce BTF supported by stable kernel. 18 | 19 | Adding this option to scripts/pahole-flags.sh. 20 | 21 | This change does not have equivalent commit in linus tree, because linus tree 22 | has support for BTF_KIND_ENUM64 tag, so it does not need to be disabled. 23 | 24 | Signed-off-by: Martin Rodriguez Reboredo 25 | Signed-off-by: Jiri Olsa 26 | Signed-off-by: Greg Kroah-Hartman 27 | [Omar: fold in "kbuild: fix up permissions on scripts/pahole-flags.sh"] 28 | Signed-off-by: Omar Sandoval 29 | --- 30 | scripts/pahole-flags.sh | 4 ++++ 31 | 1 file changed, 4 insertions(+) 32 | 33 | diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh 34 | index e6093adf4c06..7acee326aa6c 100755 35 | --- a/scripts/pahole-flags.sh 36 | +++ b/scripts/pahole-flags.sh 37 | @@ -17,4 +17,8 @@ if [ "${pahole_ver}" -ge "121" ]; then 38 | extra_paholeopt="${extra_paholeopt} --btf_gen_floats" 39 | fi 40 | 41 | +if [ "${pahole_ver}" -ge "124" ]; then 42 | + extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_enum64" 43 | +fi 44 | + 45 | echo ${extra_paholeopt} 46 | -- 47 | 2.30.2 48 | 49 | -------------------------------------------------------------------------------- /vmtest/patches/5.17-page_pool-enable-CONFIG_PAGE_POOL-by-default.patch: -------------------------------------------------------------------------------- 1 | From e74439d0a3a12b7c90826dd4f8bded6cbe0e142a Mon Sep 17 00:00:00 2001 2 | Message-ID: 3 | From: Omar Sandoval 4 | Date: Mon, 2 Jun 2025 15:26:09 -0700 5 | Subject: [PATCH] page_pool: enable CONFIG_PAGE_POOL by default 6 | 7 | Since commit b530e9e1063e ("bpf: Add "live packet" mode for XDP in 8 | BPF_PROG_RUN") (in v5.18), CONFIG_BPF_SYSCALL selects CONFIG_PAGE_POOL. 9 | Before that, only certain network drivers enabled it. In order to test 10 | page pool drgn helpers without enabling unnecessary network drivers, 11 | always enable CONFIG_PAGE_POOL. 12 | 13 | Signed-off-by: Omar Sandoval 14 | --- 15 | net/Kconfig | 1 + 16 | 1 file changed, 1 insertion(+) 17 | 18 | diff --git a/net/Kconfig b/net/Kconfig 19 | index 8a1f9d0287de..865f93e259da 100644 20 | --- a/net/Kconfig 21 | +++ b/net/Kconfig 22 | @@ -433,6 +433,7 @@ config NET_DEVLINK 23 | 24 | config PAGE_POOL 25 | bool 26 | + default y 27 | 28 | config FAILOVER 29 | tristate "Generic failover module" 30 | -- 31 | 2.49.0 32 | 33 | -------------------------------------------------------------------------------- /vmtest/patches/5.18-Revert-Makefile-link-with-z-noexecstack-no-warn-rwx-.patch: -------------------------------------------------------------------------------- 1 | From 656f46da75e198bec61e4f2cc425d9fc9b2679cc Mon Sep 17 00:00:00 2001 2 | Message-ID: <656f46da75e198bec61e4f2cc425d9fc9b2679cc.1741383572.git.osandov@osandov.com> 3 | From: Omar Sandoval 4 | Date: Fri, 7 Mar 2025 13:38:13 -0800 5 | Subject: [PATCH] Revert "Makefile: link with -z noexecstack 6 | --no-warn-rwx-segments" 7 | 8 | This reverts commit d81aa6bfff835ceea33c192d394f03e4a59cd12c. 9 | 10 | This results in a missing build ID on arm64 on v5.18-stable starting 11 | with v5.18.18 if CONFIG_MODVERSIONS=y. In mainline and LTS kernels, this 12 | was fixed properly by commit 99cb0d917ffa ("arch: fix broken BuildID for 13 | arm64 and riscv") (in v6.2), but that had a bunch of followup fixes. 14 | Commit 7b4537199a4a ("kbuild: link symbol CRCs at final link, removing 15 | CONFIG_MODULE_REL_CRCS") (in v5.19) also somehow works around it. Older 16 | stable branches didn't get this noexecstack change, so that leaves us 17 | with just v5.18 that needs this revert. 18 | 19 | Signed-off-by: Omar Sandoval 20 | --- 21 | Makefile | 5 ----- 22 | 1 file changed, 5 deletions(-) 23 | 24 | diff --git a/Makefile b/Makefile 25 | index fc7efcdab0a2..31e952dc676d 100644 26 | --- a/Makefile 27 | +++ b/Makefile 28 | @@ -1031,11 +1031,6 @@ KBUILD_CFLAGS += $(KCFLAGS) 29 | KBUILD_LDFLAGS_MODULE += --build-id=sha1 30 | LDFLAGS_vmlinux += --build-id=sha1 31 | 32 | -KBUILD_LDFLAGS += -z noexecstack 33 | -ifeq ($(CONFIG_LD_IS_BFD),y) 34 | -KBUILD_LDFLAGS += $(call ld-option,--no-warn-rwx-segments) 35 | -endif 36 | - 37 | ifeq ($(CONFIG_STRIP_ASM_SYMS),y) 38 | LDFLAGS_vmlinux += $(call ld-option, -X,) 39 | endif 40 | -- 41 | 2.48.1 42 | 43 | -------------------------------------------------------------------------------- /vmtest/patches/5.4-page_pool-enable-CONFIG_PAGE_POOL-by-default.patch: -------------------------------------------------------------------------------- 1 | From c3216ba4d0bb84edf0529e9ad99cd2ae8362a2c2 Mon Sep 17 00:00:00 2001 2 | Message-ID: 3 | From: Omar Sandoval 4 | Date: Mon, 2 Jun 2025 15:26:09 -0700 5 | Subject: [PATCH] page_pool: enable CONFIG_PAGE_POOL by default 6 | 7 | Since commit b530e9e1063e ("bpf: Add "live packet" mode for XDP in 8 | BPF_PROG_RUN") (in v5.18), CONFIG_BPF_SYSCALL selects CONFIG_PAGE_POOL. 9 | Before that, only certain network drivers enabled it. In order to test 10 | page pool drgn helpers without enabling unnecessary network drivers, 11 | always enable CONFIG_PAGE_POOL. 12 | 13 | Signed-off-by: Omar Sandoval 14 | --- 15 | net/Kconfig | 1 + 16 | 1 file changed, 1 insertion(+) 17 | 18 | diff --git a/net/Kconfig b/net/Kconfig 19 | index 48ed37cdd22f..9161d523bdd3 100644 20 | --- a/net/Kconfig 21 | +++ b/net/Kconfig 22 | @@ -435,6 +435,7 @@ config NET_DEVLINK 23 | 24 | config PAGE_POOL 25 | bool 26 | + default y 27 | 28 | config FAILOVER 29 | tristate "Generic failover module" 30 | -- 31 | 2.49.0 32 | 33 | -------------------------------------------------------------------------------- /vmtest/patches/filelock-fix-name-of-file_lease-slab-cache.patch: -------------------------------------------------------------------------------- 1 | From 3f65f3c099bcb27949e712f39ba836f21785924a Mon Sep 17 00:00:00 2001 2 | Message-ID: <3f65f3c099bcb27949e712f39ba836f21785924a.1728320011.git.osandov@fb.com> 3 | From: Omar Sandoval 4 | Date: Mon, 29 Jul 2024 15:48:12 -0700 5 | Subject: [PATCH] filelock: fix name of file_lease slab cache 6 | 7 | When struct file_lease was split out from struct file_lock, the name of 8 | the file_lock slab cache was copied to the new slab cache for 9 | file_lease. This name conflict causes confusion in /proc/slabinfo and 10 | /sys/kernel/slab. In particular, it caused failures in drgn's test case 11 | for slab cache merging. 12 | 13 | Link: https://github.com/osandov/drgn/blob/9ad29fd86499eb32847473e928b6540872d3d59a/tests/linux_kernel/helpers/test_slab.py#L81 14 | Fixes: c69ff4071935 ("filelock: split leases out of struct file_lock") 15 | Signed-off-by: Omar Sandoval 16 | Link: https://lore.kernel.org/r/2d1d053da1cafb3e7940c4f25952da4f0af34e38.1722293276.git.osandov@fb.com 17 | Reviewed-by: Chuck Lever 18 | Reviewed-by: Jeff Layton 19 | Signed-off-by: Christian Brauner 20 | --- 21 | fs/locks.c | 2 +- 22 | 1 file changed, 1 insertion(+), 1 deletion(-) 23 | 24 | diff --git a/fs/locks.c b/fs/locks.c 25 | index 9afb16e0683f..e45cad40f8b6 100644 26 | --- a/fs/locks.c 27 | +++ b/fs/locks.c 28 | @@ -2984,7 +2984,7 @@ static int __init filelock_init(void) 29 | filelock_cache = kmem_cache_create("file_lock_cache", 30 | sizeof(struct file_lock), 0, SLAB_PANIC, NULL); 31 | 32 | - filelease_cache = kmem_cache_create("file_lock_cache", 33 | + filelease_cache = kmem_cache_create("file_lease_cache", 34 | sizeof(struct file_lease), 0, SLAB_PANIC, NULL); 35 | 36 | for_each_possible_cpu(i) { 37 | -- 38 | 2.46.2 39 | 40 | -------------------------------------------------------------------------------- /vmtest/patches/kbuild-Add-skip_encoding_btf_enum64-option-to-pahole.patch: -------------------------------------------------------------------------------- 1 | From 6cfc3d5b06fe82ec7b7109f5ddfb22745475d165 Mon Sep 17 00:00:00 2001 2 | Message-Id: <6cfc3d5b06fe82ec7b7109f5ddfb22745475d165.1676498105.git.osandov@osandov.com> 3 | From: Martin Rodriguez Reboredo 4 | Date: Fri, 16 Sep 2022 14:12:34 -0300 5 | Subject: [PATCH] kbuild: Add skip_encoding_btf_enum64 option to pahole 6 | 7 | New pahole (version 1.24) generates by default new BTF_KIND_ENUM64 BTF tag, 8 | which is not supported by stable kernel. 9 | 10 | As a result the kernel with CONFIG_DEBUG_INFO_BTF option will fail to 11 | compile with following error: 12 | 13 | BTFIDS vmlinux 14 | FAILED: load BTF from vmlinux: Invalid argument 15 | 16 | New pahole provides --skip_encoding_btf_enum64 option to skip BTF_KIND_ENUM64 17 | generation and produce BTF supported by stable kernel. 18 | 19 | Adding this option to scripts/pahole-flags.sh. 20 | 21 | This change does not have equivalent commit in linus tree, because linus tree 22 | has support for BTF_KIND_ENUM64 tag, so it does not need to be disabled. 23 | 24 | Signed-off-by: Martin Rodriguez Reboredo 25 | Signed-off-by: Jiri Olsa 26 | Signed-off-by: Greg Kroah-Hartman 27 | --- 28 | scripts/pahole-flags.sh | 4 ++++ 29 | 1 file changed, 4 insertions(+) 30 | 31 | diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh 32 | index 0d99ef17e4a5..d4f3d63cb434 100755 33 | --- a/scripts/pahole-flags.sh 34 | +++ b/scripts/pahole-flags.sh 35 | @@ -20,4 +20,8 @@ if [ "${pahole_ver}" -ge "122" ]; then 36 | extra_paholeopt="${extra_paholeopt} -j" 37 | fi 38 | 39 | +if [ "${pahole_ver}" -ge "124" ]; then 40 | + extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_enum64" 41 | +fi 42 | + 43 | echo ${extra_paholeopt} 44 | -- 45 | 2.30.2 46 | 47 | -------------------------------------------------------------------------------- /vmtest/patches/kernel-reboot-emergency_restart-Set-correct-system_s.patch: -------------------------------------------------------------------------------- 1 | From 60466c067927abbcaff299845abd4b7069963139 Mon Sep 17 00:00:00 2001 2 | Message-ID: <60466c067927abbcaff299845abd4b7069963139.1748973068.git.osandov@osandov.com> 3 | From: Benjamin Bara 4 | Date: Sat, 15 Jul 2023 09:53:23 +0200 5 | Subject: [PATCH] kernel/reboot: emergency_restart: Set correct system_state 6 | 7 | As the emergency restart does not call kernel_restart_prepare(), the 8 | system_state stays in SYSTEM_RUNNING. 9 | 10 | Since bae1d3a05a8b, this hinders i2c_in_atomic_xfer_mode() from becoming 11 | active, and therefore might lead to avoidable warnings in the restart 12 | handlers, e.g.: 13 | 14 | [ 12.667612] WARNING: CPU: 1 PID: 1 at kernel/rcu/tree_plugin.h:318 rcu_note_context_switch+0x33c/0x6b0 15 | [ 12.676926] Voluntary context switch within RCU read-side critical section! 16 | ... 17 | [ 12.742376] schedule_timeout from wait_for_completion_timeout+0x90/0x114 18 | [ 12.749179] wait_for_completion_timeout from tegra_i2c_wait_completion+0x40/0x70 19 | ... 20 | [ 12.994527] atomic_notifier_call_chain from machine_restart+0x34/0x58 21 | [ 13.001050] machine_restart from panic+0x2a8/0x32c 22 | 23 | Avoid these by setting the correct system_state. 24 | 25 | Fixes: bae1d3a05a8b ("i2c: core: remove use of in_atomic()") 26 | Cc: stable@vger.kernel.org # v5.2+ 27 | Reviewed-by: Dmitry Osipenko 28 | Tested-by: Nishanth Menon 29 | Signed-off-by: Benjamin Bara 30 | Link: https://lore.kernel.org/r/20230327-tegra-pmic-reboot-v7-1-18699d5dcd76@skidata.com 31 | Signed-off-by: Lee Jones 32 | --- 33 | kernel/reboot.c | 1 + 34 | 1 file changed, 1 insertion(+) 35 | 36 | diff --git a/kernel/reboot.c b/kernel/reboot.c 37 | index 3bba88c7ffc6..6ebef11c8876 100644 38 | --- a/kernel/reboot.c 39 | +++ b/kernel/reboot.c 40 | @@ -74,6 +74,7 @@ void __weak (*pm_power_off)(void); 41 | void emergency_restart(void) 42 | { 43 | kmsg_dump(KMSG_DUMP_EMERG); 44 | + system_state = SYSTEM_RESTART; 45 | machine_emergency_restart(); 46 | } 47 | EXPORT_SYMBOL_GPL(emergency_restart); 48 | -- 49 | 2.49.0 50 | 51 | -------------------------------------------------------------------------------- /vmtest/patches/powerpc-pseries-Fix-hcall-tracepoints-with-JUMP_LABE.patch: -------------------------------------------------------------------------------- 1 | From 750bd41aeaeb1f0e0128aa4f8fcd6dd759713641 Mon Sep 17 00:00:00 2001 2 | Message-ID: <750bd41aeaeb1f0e0128aa4f8fcd6dd759713641.1709892187.git.osandov@osandov.com> 3 | From: Nicholas Piggin 4 | Date: Tue, 9 May 2023 19:15:59 +1000 5 | Subject: [PATCH] powerpc/pseries: Fix hcall tracepoints with JUMP_LABEL=n 6 | 7 | With JUMP_LABEL=n, hcall_tracepoint_refcount's address is being tested 8 | instead of its value. This results in the tracing slowpath always being 9 | taken unnecessarily. 10 | 11 | Fixes: 9a10ccb29c0a2 ("powerpc/pseries: move hcall_tracepoint_refcount out of .toc") 12 | Signed-off-by: Nicholas Piggin 13 | Signed-off-by: Michael Ellerman 14 | Link: https://msgid.link/20230509091600.70994-1-npiggin@gmail.com 15 | --- 16 | arch/powerpc/platforms/pseries/hvCall.S | 1 + 17 | 1 file changed, 1 insertion(+) 18 | 19 | diff --git a/arch/powerpc/platforms/pseries/hvCall.S b/arch/powerpc/platforms/pseries/hvCall.S 20 | index 35254ac7af5e..ca0674b0b683 100644 21 | --- a/arch/powerpc/platforms/pseries/hvCall.S 22 | +++ b/arch/powerpc/platforms/pseries/hvCall.S 23 | @@ -91,6 +91,7 @@ BEGIN_FTR_SECTION; \ 24 | b 1f; \ 25 | END_FTR_SECTION(0, 1); \ 26 | LOAD_REG_ADDR(r12, hcall_tracepoint_refcount) ; \ 27 | + ld r12,0(r12); \ 28 | std r12,32(r1); \ 29 | cmpdi r12,0; \ 30 | bne- LABEL; \ 31 | -- 32 | 2.44.0 33 | 34 | -------------------------------------------------------------------------------- /vmtest/patches/proc-kcore-allow-enabling-CONFIG_PROC_KCORE-on-ARM.patch: -------------------------------------------------------------------------------- 1 | From 67abb14dc0f5d4c9991ef6377a7b1871336cbe43 Mon Sep 17 00:00:00 2001 2 | Message-Id: <67abb14dc0f5d4c9991ef6377a7b1871336cbe43.1676535977.git.osandov@osandov.com> 3 | From: Omar Sandoval 4 | Date: Wed, 15 Feb 2023 14:54:17 -0800 5 | Subject: [PATCH] proc/kcore: allow enabling CONFIG_PROC_KCORE on ARM 6 | 7 | CONFIG_PROC_KCORE cannot be enabled on ARM since ancient history (Linux 8 | 2.6.0). See commits 5a8f43aee2ce ("[PATCH] Make /proc/kcore 9 | configurable") [1] and 72d717bfd9a9 ("[PATCH] Make modules work on ARM") 10 | [2] from the Linux kernel history tree and the patch submission for the 11 | latter [3]. However, the reasoning for it being disabled is probably no 12 | longer relevant. 13 | 14 | 1: https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id=5a8f43aee2cee75d7607c257369ab9b864795cc4 15 | 2: https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id=72d717bfd9a9e1170be2cf989ed322f85dcfe68f 16 | 3: https://lwn.net/Articles/45316/ 17 | 18 | Signed-off-by: Omar Sandoval 19 | --- 20 | fs/proc/Kconfig | 2 +- 21 | 1 file changed, 1 insertion(+), 1 deletion(-) 22 | 23 | diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig 24 | index 32b1116ae137c..12b9348b0143e 100644 25 | --- a/fs/proc/Kconfig 26 | +++ b/fs/proc/Kconfig 27 | @@ -32,3 +32,3 @@ config PROC_FS 28 | config PROC_KCORE 29 | - bool "/proc/kcore support" if !ARM 30 | + bool "/proc/kcore support" 31 | depends on PROC_FS && MMU 32 | -- 33 | 2.30.2 34 | 35 | -------------------------------------------------------------------------------- /vmtest/patches/s390-mm-make-memory_block_size_bytes-available-for-M.patch: -------------------------------------------------------------------------------- 1 | From 604ddad038bfa0ae6f447c2ff29fcd430cec8181 Mon Sep 17 00:00:00 2001 2 | Message-Id: <604ddad038bfa0ae6f447c2ff29fcd430cec8181.1676539827.git.osandov@osandov.com> 3 | From: Heiko Carstens 4 | Date: Mon, 13 Feb 2017 14:58:36 +0100 5 | Subject: [PATCH] s390/mm: make memory_block_size_bytes available for 6 | !MEMORY_HOTPLUG 7 | 8 | Fix this compile error for !MEMORY_HOTPLUG && NUMA: 9 | arch/s390/built-in.o: In function `emu_setup_size_adjust': 10 | arch/s390/numa/mode_emu.c:477: undefined reference to `memory_block_size_bytes' 11 | 12 | Signed-off-by: Heiko Carstens 13 | Signed-off-by: Martin Schwidefsky 14 | --- 15 | arch/s390/mm/init.c | 18 +++++++++--------- 16 | 1 file changed, 9 insertions(+), 9 deletions(-) 17 | 18 | diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c 19 | index ba0c8d18e10d4..ee5066718b212 100644 20 | --- a/arch/s390/mm/init.c 21 | +++ b/arch/s390/mm/init.c 22 | @@ -151,6 +151,15 @@ void __init free_initrd_mem(unsigned long start, unsigned long end) 23 | } 24 | #endif 25 | 26 | +unsigned long memory_block_size_bytes(void) 27 | +{ 28 | + /* 29 | + * Make sure the memory block size is always greater 30 | + * or equal than the memory increment size. 31 | + */ 32 | + return max_t(unsigned long, MIN_MEMORY_BLOCK_SIZE, sclp.rzm); 33 | +} 34 | + 35 | #ifdef CONFIG_MEMORY_HOTPLUG 36 | int arch_add_memory(int nid, u64 start, u64 size, bool for_device) 37 | { 38 | @@ -194,15 +203,6 @@ int arch_add_memory(int nid, u64 start, u64 size, bool for_device) 39 | return rc; 40 | } 41 | 42 | -unsigned long memory_block_size_bytes(void) 43 | -{ 44 | - /* 45 | - * Make sure the memory block size is always greater 46 | - * or equal than the memory increment size. 47 | - */ 48 | - return max_t(unsigned long, MIN_MEMORY_BLOCK_SIZE, sclp.rzm); 49 | -} 50 | - 51 | #ifdef CONFIG_MEMORY_HOTREMOVE 52 | int arch_remove_memory(u64 start, u64 size) 53 | { 54 | -- 55 | 2.30.2 56 | 57 | -------------------------------------------------------------------------------- /vmtest/patches/sched-work-around-mystery-QEMU-hang.patch: -------------------------------------------------------------------------------- 1 | From 55628334c7c73c8b948d7d97be0e79b61b28cd11 Mon Sep 17 00:00:00 2001 2 | Message-ID: <55628334c7c73c8b948d7d97be0e79b61b28cd11.1699049719.git.osandov@fb.com> 3 | From: Omar Sandoval 4 | Date: Fri, 3 Nov 2023 14:50:46 -0700 5 | Subject: [PATCH] sched: work around mystery QEMU hang 6 | 7 | Before commit 2558aacff858 ("sched/hotplug: Ensure only per-cpu kthreads 8 | run during hotplug") (in v5.11), GCC inlined the call to 9 | raw_spin_unlock_irq() after __balance_callbacks() in __schedule(). That 10 | change incidentally turned it into an uninlined function call. For some 11 | reason, this causes the kernel to hang while calibrating loops_per_jiffy 12 | for secondary CPUs, but only under QEMU full system emulation, not KVM. 13 | 14 | Commit 5cb9eaa3d274 ("sched: Wrap rq::lock access") (in v5.14) added 15 | some wrappers that essentially turned the call to raw_spin_unlock_irq() 16 | into a (still not inlined by GCC) call to raw_spin_unlock() followed by 17 | local_irq_enable(). This mysteriously gets rid of the hang. 18 | 19 | This feels like a QEMU bug, but since it only affects EOL kernels, let's 20 | just work around it. 21 | 22 | See osandov/drgn#365. 23 | 24 | Signed-off-by: Omar Sandoval 25 | --- 26 | kernel/sched/core.c | 3 ++- 27 | 1 file changed, 2 insertions(+), 1 deletion(-) 28 | 29 | diff --git a/kernel/sched/core.c b/kernel/sched/core.c 30 | index 4ca80df205ce..4901f47e84d8 100644 31 | --- a/kernel/sched/core.c 32 | +++ b/kernel/sched/core.c 33 | @@ -5150,7 +5150,8 @@ static void __sched notrace __schedule(bool preempt) 34 | 35 | rq_unpin_lock(rq, &rf); 36 | __balance_callbacks(rq); 37 | - raw_spin_unlock_irq(&rq->lock); 38 | + raw_spin_unlock(&rq->lock); 39 | + local_irq_enable(); 40 | } 41 | } 42 | 43 | -- 44 | 2.41.0 45 | 46 | --------------------------------------------------------------------------------