├── .github ├── scripts │ ├── build-debian.sh │ ├── build-kernel.sh │ ├── build-pahole.sh │ ├── run-selftests.sh │ └── travis_wait.bash └── workflows │ ├── build.yml │ ├── lint.yml │ ├── ondemand.yml │ ├── test.yml │ └── vmtest.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── COPYING ├── MANIFEST ├── NEWS ├── PKG-MAINTAINERS ├── README ├── README.DEBUG ├── README.btf ├── README.cross ├── README.ctracer ├── README.tarball ├── btf_encoder.c ├── btf_encoder.h ├── btf_loader.c ├── btfdiff ├── buildcmd.sh ├── changes-v1.13 ├── changes-v1.16 ├── changes-v1.17 ├── changes-v1.18 ├── changes-v1.19 ├── changes-v1.20 ├── changes-v1.21 ├── changes-v1.22 ├── changes-v1.23 ├── changes-v1.24 ├── changes-v1.25 ├── changes-v1.26 ├── changes-v1.27 ├── changes-v1.28 ├── changes-v1.29 ├── changes-v1.30 ├── cmake └── modules │ ├── FindDWARF.cmake │ ├── Findargp.cmake │ └── Findobstack.cmake ├── codiff.c ├── config.h.cmake ├── ctf.h ├── ctf_encoder.c ├── ctf_encoder.h ├── ctf_loader.c ├── ctfdwdiff ├── ctracer.c ├── dtagnames.c ├── dutil.c ├── dutil.h ├── dwarf_loader.c ├── dwarves.c ├── dwarves.h ├── dwarves_emit.c ├── dwarves_emit.h ├── dwarves_fprintf.c ├── dwarves_reorganize.c ├── dwarves_reorganize.h ├── elf_symtab.c ├── elf_symtab.h ├── elfcreator.c ├── elfcreator.h ├── fullcircle ├── gobuffer.c ├── gobuffer.h ├── hash.h ├── lib ├── Makefile ├── ctracer_relay.c ├── ctracer_relay.h ├── include │ └── bpf └── linux.blacklist.cu ├── libctf.c ├── libctf.h ├── list.h ├── man-pages └── pahole.1 ├── ostra ├── ostra-cg └── python │ └── ostra.py ├── pahole.c ├── pdwtags.c ├── pfunct.c ├── pglobal.c ├── prefcnt.c ├── rbtree.c ├── rbtree.h ├── regtest ├── rpm └── SPECS │ └── dwarves.spec ├── scncopy.c ├── syscse.c └── tests ├── btf_functions.sh ├── default_vmlinux_btf.sh ├── flexible_arrays.sh ├── pfunct-btf-decl-tags.sh ├── prettify_perf.data.sh ├── reproducible_build.sh └── tests /.github/scripts/build-debian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # Copyright (c) 2025, Oracle and/or its affiliates. 5 | # 6 | 7 | PHASES=(${@:-SETUP RUN CLEANUP}) 8 | DEBIAN_RELEASE="${DEBIAN_RELEASE:-testing}" 9 | CONT_NAME="${CONT_NAME:-dwarves-debian-$DEBIAN_RELEASE}" 10 | ENV_VARS="${ENV_VARS:-}" 11 | DOCKER_RUN="${DOCKER_RUN:-docker run}" 12 | REPO_ROOT="${REPO_ROOT:-$PWD}" 13 | ADDITIONAL_DEPS=(pkgconf) 14 | EXTRA_CFLAGS="" 15 | EXTRA_LDFLAGS="" 16 | 17 | function info() { 18 | echo -e "\033[33;1m$1\033[0m" 19 | } 20 | 21 | function error() { 22 | echo -e "\033[31;1m$1\033[0m" 23 | } 24 | 25 | function docker_exec() { 26 | docker exec $ENV_VARS $CONT_NAME "$@" 27 | } 28 | 29 | set -eu 30 | 31 | source "$(dirname $0)/travis_wait.bash" 32 | 33 | for phase in "${PHASES[@]}"; do 34 | case $phase in 35 | SETUP) 36 | info "Setup phase" 37 | info "Using Debian $DEBIAN_RELEASE" 38 | 39 | docker --version 40 | 41 | docker pull debian:$DEBIAN_RELEASE 42 | info "Starting container $CONT_NAME" 43 | $DOCKER_RUN -v $REPO_ROOT:/build:rw \ 44 | -w /build --privileged=true --name $CONT_NAME \ 45 | -dit --net=host debian:$DEBIAN_RELEASE /bin/bash 46 | echo -e "::group::Build Env Setup" 47 | 48 | docker_exec apt-get -y update 49 | docker_exec apt-get -y install aptitude 50 | docker_exec aptitude -y install make cmake libz-dev libelf-dev libdw-dev git 51 | docker_exec aptitude -y install "${ADDITIONAL_DEPS[@]}" 52 | echo -e "::endgroup::" 53 | ;; 54 | RUN|RUN_CLANG|RUN_CLANG16|RUN_GCC12) 55 | CC="cc" 56 | if [[ "$phase" =~ "RUN_CLANG(\d+)(_ASAN)?" ]]; then 57 | ENV_VARS="-e CC=clang-${BASH_REMATCH[1]} -e CXX=clang++-${BASH_REMATCH[1]}" 58 | CC="clang-${BASH_REMATCH[1]}" 59 | elif [[ "$phase" = *"CLANG"* ]]; then 60 | ENV_VARS="-e CC=clang -e CXX=clang++" 61 | CC="clang" 62 | elif [[ "$phase" =~ "RUN_GCC(\d+)(_ASAN)?" ]]; then 63 | ENV_VARS="-e CC=gcc-${BASH_REMATCH[1]} -e CXX=g++-${BASH_REMATCH[1]}" 64 | CC="gcc-${BASH_REMATCH[1]}" 65 | fi 66 | if [[ "$CC" != "cc" ]]; then 67 | docker_exec aptitude -y install "$CC" 68 | else 69 | docker_exec aptitude -y install gcc 70 | fi 71 | git config --global --add safe.directory $REPO_ROOT 72 | pushd $REPO_ROOT 73 | git submodule update --init 74 | popd 75 | docker_exec mkdir build install 76 | docker_exec ${CC} --version 77 | info "build" 78 | docker_exec cmake -DGIT_SUBMODULE=OFF . 79 | docker_exec make -j$((4*$(nproc))) 80 | info "install" 81 | docker_exec make DESTDIR=../install install 82 | ;; 83 | CLEANUP) 84 | info "Cleanup phase" 85 | docker stop $CONT_NAME 86 | docker rm -f $CONT_NAME 87 | ;; 88 | *) 89 | echo >&2 "Unknown phase '$phase'" 90 | exit 1 91 | esac 92 | done 93 | -------------------------------------------------------------------------------- /.github/scripts/build-kernel.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # Copyright (c) 2025, Oracle and/or its affiliates. 5 | # 6 | 7 | GITHUB_WORKSPACE=${GITHUB_WORKSPACE:-$(dirname $0)/../..} 8 | INPUTS_ARCH=${INPUTS_ARCH:-$(uname -m)} 9 | REPO=${REPO:-https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git} 10 | REPO_BRANCH=${REPO_BRANCH:-master} 11 | REPO_TARGET=${GITHUB_WORKSPACE}/.kernel 12 | 13 | export PATH=${GITHUB_WORKSPACE}/install/usr/local/bin:${PATH} 14 | export PAHOLE=${GITHUB_WORKSPACE}/install/usr/local/bin/pahole 15 | 16 | which pahole 17 | $PAHOLE --version 18 | 19 | if [[ ! -d $REPO_TARGET ]]; then 20 | git clone $REPO $REPO_TARGET 21 | fi 22 | cd $REPO_TARGET 23 | git checkout $REPO_BRANCH 24 | 25 | cat tools/testing/selftests/bpf/config \ 26 | tools/testing/selftests/bpf/config.${INPUTS_ARCH} > .config 27 | # this file might or might not exist depending on kernel version 28 | if [[ -f tools/testing/selftests/bpf/config.vm ]]; then 29 | cat tools/testing/selftests/bpf/config.vm >> .config 30 | fi 31 | make olddefconfig && make prepare 32 | cat .config 33 | make -j $((4*$(nproc))) all 34 | 35 | -------------------------------------------------------------------------------- /.github/scripts/build-pahole.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # Copyright (c) 2025, Oracle and/or its affiliates. 5 | # 6 | 7 | GITHUB_WORKSPACE=${GITHUB_WORKSPACE:-$(dirname $0)/../..} 8 | cd $GITHUB_WORKSPACE 9 | git config --global --add safe.directory $GITHUB_WORKSPACE 10 | git submodule update --init 11 | mkdir -p build 12 | cd build 13 | pwd 14 | cmake -DGIT_SUBMODULE=OFF -DBUILD_SHARED_LIBS=OFF .. 15 | make -j$((4*$(nproc))) all 16 | make DESTDIR=../install install 17 | 18 | -------------------------------------------------------------------------------- /.github/scripts/run-selftests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # Copyright (c) 2025, Oracle and/or its affiliates. 5 | # 6 | 7 | GITHUB_WORKSPACE=${GITHUB_WORKSPACE:-$(pwd)} 8 | VMLINUX=${GITHUB_WORKSPACE}/.kernel/vmlinux 9 | SELFTESTS=${GITHUB_WORKSPACE}/tests 10 | cd $SELFTESTS 11 | export PATH=${GITHUB_WORKSPACE}/install/usr/local/bin:${PATH} 12 | which pahole 13 | pahole --version 14 | vmlinux=$VMLINUX ./tests 15 | 16 | -------------------------------------------------------------------------------- /.github/scripts/travis_wait.bash: -------------------------------------------------------------------------------- 1 | # This was borrowed from https://github.com/travis-ci/travis-build/tree/master/lib/travis/build/bash 2 | # to get around https://github.com/travis-ci/travis-ci/issues/9979. It should probably be removed 3 | # as soon as Travis CI has started to provide an easy way to export the functions to bash scripts. 4 | 5 | travis_jigger() { 6 | local cmd_pid="${1}" 7 | shift 8 | local timeout="${1}" 9 | shift 10 | local count=0 11 | 12 | echo -e "\\n" 13 | 14 | while [[ "${count}" -lt "${timeout}" ]]; do 15 | count="$((count + 1))" 16 | echo -ne "Still running (${count} of ${timeout}): ${*}\\r" 17 | sleep 60 18 | done 19 | 20 | echo -e "\\n${ANSI_RED}Timeout (${timeout} minutes) reached. Terminating \"${*}\"${ANSI_RESET}\\n" 21 | kill -9 "${cmd_pid}" 22 | } 23 | 24 | travis_wait() { 25 | local timeout="${1}" 26 | 27 | if [[ "${timeout}" =~ ^[0-9]+$ ]]; then 28 | shift 29 | else 30 | timeout=20 31 | fi 32 | 33 | local cmd=("${@}") 34 | local log_file="travis_wait_${$}.log" 35 | 36 | "${cmd[@]}" &>"${log_file}" & 37 | local cmd_pid="${!}" 38 | 39 | travis_jigger "${!}" "${timeout}" "${cmd[@]}" & 40 | local jigger_pid="${!}" 41 | local result 42 | 43 | { 44 | set +e 45 | wait "${cmd_pid}" 2>/dev/null 46 | result="${?}" 47 | ps -p"${jigger_pid}" &>/dev/null && kill "${jigger_pid}" 48 | set -e 49 | } 50 | 51 | if [[ "${result}" -eq 0 ]]; then 52 | echo -e "\\n${ANSI_GREEN}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}" 53 | else 54 | echo -e "\\n${ANSI_RED}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}" 55 | fi 56 | 57 | echo -e "\\n${ANSI_GREEN}Log:${ANSI_RESET}\\n" 58 | cat "${log_file}" 59 | 60 | return "${result}" 61 | } 62 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: dwarves-build 2 | 3 | on: 4 | pull_request: 5 | push: 6 | schedule: 7 | - cron: '0 18 * * *' 8 | 9 | concurrency: 10 | group: ci-build-${{ github.head_ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | 15 | debian: 16 | runs-on: ubuntu-latest 17 | name: Debian Build (${{ matrix.name }}) 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | include: 22 | - name: default 23 | target: RUN 24 | - name: gcc-12 25 | target: RUN_GCC12 26 | - name: clang 27 | target: RUN_CLANG 28 | steps: 29 | - uses: actions/checkout@v4 30 | name: Checkout 31 | - name: setup 32 | shell: bash 33 | run: ./.github/scripts/build-debian.sh SETUP ${{ matrix.target }} 34 | 35 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: "lint" 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - next 9 | 10 | jobs: 11 | shellcheck: 12 | name: ShellCheck 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v4 17 | - name: Run ShellCheck 18 | uses: ludeeus/action-shellcheck@master 19 | env: 20 | SHELLCHECK_OPTS: --severity=error 21 | -------------------------------------------------------------------------------- /.github/workflows/ondemand.yml: -------------------------------------------------------------------------------- 1 | name: ondemand 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | arch: 7 | default: 'x86_64' 8 | required: true 9 | llvm-version: 10 | default: '18' 11 | required: true 12 | kernel: 13 | default: 'LATEST' 14 | required: true 15 | pahole: 16 | default: "master" 17 | required: true 18 | runs-on: 19 | default: 'ubuntu-24.04' 20 | required: true 21 | 22 | jobs: 23 | vmtest: 24 | name: ${{ inputs.kernel }} kernel llvm-${{ inputs.llvm-version }} pahole@${{ inputs.pahole }} 25 | uses: ./.github/workflows/vmtest.yml 26 | with: 27 | runs_on: ${{ inputs.runs-on }} 28 | kernel: ${{ inputs.kernel }} 29 | arch: ${{ inputs.arch }} 30 | llvm-version: ${{ inputs.llvm-version }} 31 | pahole: ${{ inputs.pahole }} 32 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: dwarves-ci 2 | 3 | on: 4 | pull_request: 5 | push: 6 | schedule: 7 | - cron: '0 18 * * *' 8 | 9 | concurrency: 10 | group: ci-test-${{ github.head_ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | vmtest: 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | include: 19 | - kernel: 'LATEST' 20 | runs_on: 'ubuntu-24.04' 21 | arch: 'x86_64' 22 | llvm-version: '18' 23 | pahole: 'master' 24 | - kernel: 'LATEST' 25 | runs_on: 'ubuntu-24.04-arm' 26 | arch: 'aarch64' 27 | llvm-version: '18' 28 | pahole: 'tmp.master' 29 | name: Linux ${{ matrix.kernel }} 30 | uses: ./.github/workflows/vmtest.yml 31 | with: 32 | runs_on: ${{ matrix.runs_on }} 33 | kernel: ${{ matrix.kernel }} 34 | arch: ${{ matrix.arch }} 35 | llvm-version: ${{ matrix.llvm-version }} 36 | pahole: ${{ matrix.pahole }} 37 | -------------------------------------------------------------------------------- /.github/workflows/vmtest.yml: -------------------------------------------------------------------------------- 1 | name: 'Build kernel run selftests via vmtest' 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | runs_on: 7 | required: true 8 | default: 'ubuntu-24.04' 9 | type: string 10 | arch: 11 | description: 'what arch to test' 12 | required: true 13 | default: 'x86_64' 14 | type: string 15 | kernel: 16 | description: 'kernel version or LATEST' 17 | required: true 18 | default: 'LATEST' 19 | type: string 20 | pahole: 21 | description: 'pahole rev or branch' 22 | required: false 23 | default: 'master' 24 | type: string 25 | llvm-version: 26 | description: 'llvm version' 27 | required: false 28 | default: '18' 29 | type: string 30 | jobs: 31 | vmtest: 32 | name: pahole@${{ inputs.arch }} 33 | runs-on: ${{ inputs.runs_on }} 34 | steps: 35 | 36 | - uses: actions/checkout@v4 37 | 38 | - name: Setup environment 39 | uses: libbpf/ci/setup-build-env@v3 40 | with: 41 | pahole: ${{ inputs.pahole }} 42 | arch: ${{ inputs.arch }} 43 | llvm-version: ${{ inputs.llvm-version }} 44 | 45 | - name: Build,install current pahole 46 | shell: bash 47 | run: .github/scripts/build-pahole.sh 48 | 49 | - name: Get kernel source 50 | uses: libbpf/ci/get-linux-source@v3 51 | with: 52 | repo: 'https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git' 53 | dest: '${{ github.workspace }}/.kernel' 54 | 55 | - name: Configure, build kernel with current pahole 56 | shell: bash 57 | run: .github/scripts/build-kernel.sh 58 | 59 | - name: Run selftests 60 | shell: bash 61 | run: .github/scripts/run-selftests.sh 62 | 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /config.h 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/bpf"] 2 | path = lib/bpf 3 | url = https://github.com/libbpf/libbpf 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(pahole C) 3 | cmake_policy(SET CMP0005 NEW) 4 | 5 | option(LIBBPF_EMBEDDED "Use the embedded version of libbpf instead of searching it via pkg-config" ON) 6 | if (NOT LIBBPF_EMBEDDED) 7 | find_package(PkgConfig REQUIRED) 8 | if(PKGCONFIG_FOUND) 9 | pkg_check_modules(LIBBPF REQUIRED libbpf>=0.4.0) 10 | endif() 11 | endif() 12 | 13 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} 14 | ${CMAKE_CURRENT_SOURCE_DIR}) 15 | if(NOT LIBBPF_FOUND) 16 | # Allows to use 'system' style #include with both embedded and system libbpf 17 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/lib/include) 18 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi) 19 | else() 20 | INCLUDE_DIRECTORIES(${LIBBPF_INCLUDE_DIRS}) 21 | LINK_DIRECTORIES(${LIBBPF_LIBRARY_DIRS}) 22 | endif() 23 | 24 | # Use the standard library installation directory 25 | include(GNUInstallDirs) 26 | set(CMAKE_INSTALL_LIBDIR "lib" CACHE STRING "libdir name") 27 | 28 | # where to look first for cmake modules, 29 | # before ${CMAKE_ROOT}/Modules/ is checked 30 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") 31 | 32 | if (NOT CMAKE_BUILD_TYPE) 33 | set (CMAKE_BUILD_TYPE Debug CACHE STRING 34 | "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." 35 | FORCE) 36 | endif (NOT CMAKE_BUILD_TYPE) 37 | 38 | set(CMAKE_C_FLAGS_DEBUG "-Wall -Werror -ggdb -O0") 39 | set(CMAKE_C_FLAGS_RELEASE "-Wall -O2") 40 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") 41 | 42 | if (NOT DEFINED BUILD_SHARED_LIBS) 43 | set (BUILD_SHARED_LIBS ON) 44 | message(STATUS "Setting BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS}") 45 | endif (NOT DEFINED BUILD_SHARED_LIBS) 46 | 47 | # Just for grepping, DWARVES_VERSION isn't used anywhere anymore 48 | # add_definitions(-D_GNU_SOURCE -DDWARVES_VERSION="v1.30") 49 | add_definitions(-D_GNU_SOURCE -DDWARVES_MAJOR_VERSION=1) 50 | add_definitions(-D_GNU_SOURCE -DDWARVES_MINOR_VERSION=30) 51 | find_package(DWARF REQUIRED) 52 | find_package(ZLIB REQUIRED) 53 | find_package(argp REQUIRED) 54 | find_package(obstack REQUIRED) 55 | find_package(Python3 QUIET) 56 | 57 | # make sure git submodule(s) are checked out 58 | find_package(Git QUIET) 59 | if(LIBBPF_EMBEDDED AND GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") 60 | # Update submodules as needed 61 | option(GIT_SUBMODULE "Check submodules during build" ON) 62 | if(GIT_SUBMODULE) 63 | message(STATUS "Submodule update") 64 | execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive 65 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 66 | RESULT_VARIABLE GIT_SUBMOD_RESULT) 67 | if(NOT GIT_SUBMOD_RESULT EQUAL "0") 68 | message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") 69 | else() 70 | message(STATUS "Submodule update - done") 71 | endif() 72 | endif() 73 | endif() 74 | if(NOT LIBBPF_FOUND AND NOT EXISTS "${PROJECT_SOURCE_DIR}/lib/bpf/src/btf.h") 75 | message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") 76 | endif() 77 | 78 | if (NOT DEFINED LIB_INSTALL_DIR) 79 | set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") 80 | endif() 81 | 82 | # libbpf uses reallocarray, which is not available in all versions of glibc 83 | # libbpf's include/tools/libc_compat.h provides implementation, but needs 84 | # COMPACT_NEED_REALLOCARRAY to be set 85 | INCLUDE(CheckCSourceCompiles) 86 | CHECK_C_SOURCE_COMPILES( 87 | " 88 | #define _GNU_SOURCE 89 | #include 90 | int main(void) 91 | { 92 | return !!reallocarray(NULL, 1, 1); 93 | } 94 | " HAVE_REALLOCARRAY_SUPPORT) 95 | if (NOT HAVE_REALLOCARRAY_SUPPORT) 96 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCOMPAT_NEED_REALLOCARRAY") 97 | endif() 98 | 99 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64") 100 | 101 | if (NOT LIBBPF_FOUND) 102 | file(GLOB libbpf_sources "lib/bpf/src/*.c") 103 | add_library(bpf OBJECT ${libbpf_sources}) 104 | set_property(TARGET bpf PROPERTY POSITION_INDEPENDENT_CODE 1) 105 | target_include_directories(bpf PRIVATE 106 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include 107 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi) 108 | endif() 109 | 110 | set(dwarves_LIB_SRCS dwarves.c dwarves_fprintf.c gobuffer.c 111 | ctf_loader.c libctf.c btf_encoder.c btf_loader.c 112 | dwarf_loader.c dutil.c elf_symtab.c rbtree.c) 113 | if (NOT LIBBPF_FOUND) 114 | list(APPEND dwarves_LIB_SRCS $) 115 | endif() 116 | add_library(dwarves ${dwarves_LIB_SRCS}) 117 | set_target_properties(dwarves PROPERTIES VERSION 1.0.0 SOVERSION 1) 118 | set_target_properties(dwarves PROPERTIES INTERFACE_LINK_LIBRARIES "") 119 | target_link_libraries(dwarves ${DWARF_LIBRARIES} ${ZLIB_LIBRARIES} ${LIBBPF_LIBRARIES} ${ARGP_LIBRARY} ${OBSTACK_LIBRARY}) 120 | 121 | set(dwarves_emit_LIB_SRCS dwarves_emit.c) 122 | add_library(dwarves_emit ${dwarves_emit_LIB_SRCS}) 123 | set_target_properties(dwarves_emit PROPERTIES VERSION 1.0.0 SOVERSION 1) 124 | target_link_libraries(dwarves_emit dwarves) 125 | 126 | set(dwarves_reorganize_LIB_SRCS dwarves_reorganize.c) 127 | add_library(dwarves_reorganize ${dwarves_reorganize_LIB_SRCS}) 128 | set_target_properties(dwarves_reorganize PROPERTIES VERSION 1.0.0 SOVERSION 1) 129 | target_link_libraries(dwarves_reorganize dwarves) 130 | 131 | set(codiff_SRCS codiff.c) 132 | add_executable(codiff ${codiff_SRCS}) 133 | target_link_libraries(codiff dwarves) 134 | 135 | set(ctracer_SRCS ctracer.c) 136 | add_executable(ctracer ${ctracer_SRCS}) 137 | target_link_libraries(ctracer dwarves dwarves_emit dwarves_reorganize ${ELF_LIBRARY}) 138 | 139 | set(dtagnames_SRCS dtagnames.c) 140 | add_executable(dtagnames ${dtagnames_SRCS}) 141 | target_link_libraries(dtagnames dwarves) 142 | 143 | set(pahole_SRCS pahole.c) 144 | add_executable(pahole ${pahole_SRCS}) 145 | target_link_libraries(pahole dwarves dwarves_emit dwarves_reorganize) 146 | 147 | set(pdwtags_SRCS pdwtags.c) 148 | add_executable(pdwtags ${pdwtags_SRCS}) 149 | target_link_libraries(pdwtags dwarves) 150 | 151 | set(pglobal_SRCS pglobal.c) 152 | add_executable(pglobal ${pglobal_SRCS}) 153 | target_link_libraries(pglobal dwarves) 154 | 155 | set(pfunct_SRCS pfunct.c) 156 | add_executable(pfunct ${pfunct_SRCS}) 157 | target_link_libraries(pfunct dwarves dwarves_emit ${ELF_LIBRARY}) 158 | 159 | set(prefcnt_SRCS prefcnt.c) 160 | add_executable(prefcnt ${prefcnt_SRCS}) 161 | target_link_libraries(prefcnt dwarves) 162 | 163 | set(scncopy_SRCS scncopy.c elfcreator.c) 164 | add_executable(scncopy ${scncopy_SRCS}) 165 | target_link_libraries(scncopy dwarves ${ELF_LIBRARY}) 166 | 167 | set(syscse_SRCS syscse.c) 168 | add_executable(syscse ${syscse_SRCS}) 169 | target_link_libraries(syscse dwarves) 170 | 171 | install(TARGETS codiff ctracer dtagnames pahole pdwtags 172 | pfunct pglobal prefcnt scncopy syscse RUNTIME DESTINATION 173 | ${CMAKE_INSTALL_PREFIX}/bin) 174 | install(TARGETS dwarves LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) 175 | install(TARGETS dwarves dwarves_emit dwarves_reorganize LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) 176 | install(FILES dwarves.h dwarves_emit.h dwarves_reorganize.h 177 | dutil.h gobuffer.h list.h rbtree.h 178 | btf_encoder.h config.h ctf.h 179 | elfcreator.h elf_symtab.h hash.h libctf.h 180 | DESTINATION ${CMAKE_INSTALL_PREFIX}/include/dwarves/) 181 | install(FILES man-pages/pahole.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1/) 182 | if(Python3_FOUND) 183 | install(PROGRAMS ostra/ostra-cg DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) 184 | install(FILES ostra/python/ostra.py DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dwarves/runtime/python) 185 | endif() 186 | install(PROGRAMS btfdiff fullcircle DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) 187 | install(FILES lib/Makefile lib/ctracer_relay.c lib/ctracer_relay.h lib/linux.blacklist.cu 188 | DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dwarves/runtime) 189 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | config.h.cmake 2 | btfdiff 3 | btf_encoder.c 4 | btf_encoder.h 5 | btf_loader.c 6 | ctf_encoder.c 7 | ctf_encoder.h 8 | ctf_loader.c 9 | dwarf_loader.c 10 | dwarves.c 11 | dwarves.h 12 | dwarves_emit.c 13 | dwarves_emit.h 14 | dwarves_fprintf.c 15 | dwarves_reorganize.c 16 | dwarves_reorganize.h 17 | cmake/modules/FindDWARF.cmake 18 | cmake/modules/Findargp.cmake 19 | cmake/modules/Findobstack.cmake 20 | CMakeLists.txt 21 | codiff.c 22 | ctracer.c 23 | dtagnames.c 24 | elfcreator.c 25 | elfcreator.h 26 | elf_symtab.c 27 | elf_symtab.h 28 | fullcircle 29 | gobuffer.c 30 | gobuffer.h 31 | hash.h 32 | list.h 33 | MANIFEST 34 | man-pages/pahole.1 35 | pahole.c 36 | pdwtags.c 37 | pfunct.c 38 | pglobal.c 39 | prefcnt.c 40 | rbtree.c 41 | rbtree.h 42 | scncopy.c 43 | syscse.c 44 | dutil.c 45 | dutil.h 46 | changes-v1.13 47 | changes-v1.16 48 | changes-v1.17 49 | changes-v1.18 50 | changes-v1.19 51 | changes-v1.20 52 | changes-v1.21 53 | changes-v1.22 54 | changes-v1.23 55 | changes-v1.24 56 | changes-v1.25 57 | changes-v1.26 58 | changes-v1.27 59 | changes-v1.28 60 | changes-v1.29 61 | changes-v1.30 62 | buildcmd.sh 63 | COPYING 64 | NEWS 65 | README 66 | README.DEBUG 67 | README.btf 68 | README.ctracer 69 | README.tarball 70 | rpm/SPECS/dwarves.spec 71 | lib/Makefile 72 | lib/ctracer_relay.c 73 | lib/ctracer_relay.h 74 | lib/linux.blacklist.cu 75 | ostra/ostra-cg 76 | ostra/python/ostra.py 77 | ctf.h 78 | libctf.c 79 | libctf.h 80 | regtest 81 | lib/bpf/ 82 | lib/include/bpf 83 | -------------------------------------------------------------------------------- /PKG-MAINTAINERS: -------------------------------------------------------------------------------- 1 | # Please let me know if I should remove/update/add more distro package maintainers here 2 | # I'm keeping this so that I CC them when releasing new versions, thanks! 3 | 4 | Jan Alexander Steffens 5 | Domenico Andreoli 6 | Matthias Schwarzott 7 | Dominique Leuenberger 8 | Dominique Martinet 9 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Build instructions: 2 | 3 | 1. install cmake 4 | 2. mkdir build 5 | 3. cd build 6 | 4. cmake .. 7 | 5. make install 8 | 9 | cmake Options: 10 | -DBUILD_SHARED_LIBS 11 | By default SHARED libraries are created and applications are linked to it. 12 | Use -DBUILD_SHARED_LIBS=OFF while invoking cmake to create STATIC libraries 13 | and link applications to it. 14 | 15 | Ex. cmake -DBUILD_SHARED_LIBS=OFF .. 16 | 17 | -DCMAKE_INSTALL_PREFIX 18 | Default is to install to /usr/local, use -DCMAKE_INSTALL_PREFIX= 19 | when invoking cmake to specify another install location. 20 | 21 | You may need to update the libbpf git submodule: 22 | 23 | git submodule update --init --recursive 24 | 25 | Testing: 26 | 27 | Tests are available in the tests subdirectory and should be run prior to 28 | submitting patches. Patches that add functionality should add to tests 29 | here also. Tests can be run by 30 | 31 | - running the scripts directly using a pre-existing vmlinux binary; i.e. 32 | cd tests ; vmlinux=/path/2/vmlinux ./tests 33 | (the vmlinux binary must contain DWARF to be converted to BTF) 34 | 35 | - running the tests via local scripts in .github/scripts; i.e. 36 | bash .github/scripts/build-pahole.sh; \ 37 | bash .github/scripts/build-kernel.sh; \ 38 | bash .github/scripts/run-selftests.sh 39 | - via GitHub actions: push a branch to a GitHub repo; actions will be 40 | triggered for build and test matching the above steps. See the "Actions" 41 | tab in the github repo for info on job pass/fail and logs. 42 | -------------------------------------------------------------------------------- /README.DEBUG: -------------------------------------------------------------------------------- 1 | rm -rf build 2 | mkdir build 3 | cd build 4 | cmake -DCMAKE_BUILD_TYPE=Debug .. 5 | cd .. 6 | make -C build 7 | -------------------------------------------------------------------------------- /README.cross: -------------------------------------------------------------------------------- 1 | CC=s390x-linux-gnu-gcc \ 2 | cmake -DDWARF_INCLUDE_DIR=/usr/s390x-linux-gnu/include/ \ 3 | -DLIBDW_INCLUDE_DIR=/usr/s390x-linux-gnu/include/ \ 4 | -DDWARF_LIBRARY=/usr/s390x-linux-gnu/lib/libdw.so.1 \ 5 | -DELF_LIBRARY=/usr/s390x-linux-gnu/lib/libelf.so.1 \ 6 | -DZLIB_LIBRARY=/usr/s390x-linux-gnu/lib/libz.so.1 \ 7 | -DZLIB_INCLUDE_DIR=/usr/s390x-linux-gnu/include/ .. 8 | -------------------------------------------------------------------------------- /README.ctracer: -------------------------------------------------------------------------------- 1 | Basic instructions to use ctracer: 2 | 3 | 1. Install dwarves, if you are not that excited about building it I'm 4 | keeping rpms for Fedora Core 6 here: 5 | 6 | http://oops.ghostprotocols.net:81/acme/dwarves/rpm/ 7 | 8 | The .src.rpm is there in case you want to rebuild it for another 9 | rpm based distro. 10 | 11 | Since fedora 9 you just have to run: 12 | 13 | yum install dwarves 14 | 15 | 2. build the kernel with CONFIG_DEBUG_INFO=y, i.e. gcc -g, that will 16 | insert the DWARF info needed by all the pahole tools, ctracer, etc, or 17 | just install the kernel-debuginfo rpm package on FC6, other distros 18 | have it with a different name, its just the kernel built with debug 19 | info. 20 | 21 | 3. Assuming you installed the kernel-debuginfo package, to run ctracer 22 | on your workstation, just do the following steps: 23 | 24 | mkdir foo 25 | cd foo 26 | ln -s /usr/share/dwarves/runtime/* . 27 | make CLASS=sock # to trace struct sock methods, this one is safe, try others 28 | # and tell me your horror (or success :-) ) story. 29 | 30 | (kbuild gurus, send suggestions to simplify this procedure! :-) ) 31 | 32 | 4. load the resulting module: 33 | 34 | insmod ctracer.ko 35 | 36 | dmesg will show how many probes were successfully installed 37 | 38 | 5. Do some related activity (ssh, in the above example should do) 39 | 40 | 6. Make sure debugfs is mounted 41 | 42 | [root@filo ~]# mount -t debugfs none_debugfs /sys/kernel/debug/ 43 | 44 | 7. Get the log: 45 | 46 | cat /sys/kernel/debug/ctracer0 > /tmp/ctracer.log 47 | 48 | 8. Generate the callgraph! 49 | 50 | make callgraph 51 | 52 | 9. rmmod ctracer 53 | 54 | Change the shipped Makefile accordingly to build a module for qemu or another test 55 | machine. 56 | 57 | The relay transport is mostly ready and will be included in the upcoming changesets. 58 | -------------------------------------------------------------------------------- /README.tarball: -------------------------------------------------------------------------------- 1 | v=1.$(($(git tag | sort -V | tail -1 | cut -d. -f2) + 1)) ; tar cvfJ ~/rpmbuild/SOURCES/dwarves-${v}.tar.xz --transform "s,^pahole/,dwarves-${v}/," `sed s%^%../pahole/%g MANIFEST` 2 | -------------------------------------------------------------------------------- /btf_encoder.h: -------------------------------------------------------------------------------- 1 | #ifndef _BTF_ENCODER_H_ 2 | #define _BTF_ENCODER_H_ 1 3 | /* 4 | SPDX-License-Identifier: GPL-2.0-only 5 | 6 | Copyright (C) 2019 Facebook 7 | 8 | Derived from ctf_encoder.h, which is: 9 | Copyright (C) Arnaldo Carvalho de Melo 10 | */ 11 | 12 | #include 13 | 14 | struct btf_encoder; 15 | struct btf; 16 | struct cu; 17 | struct list_head; 18 | 19 | /* Bit flags specifying which kinds of variables are emitted */ 20 | enum btf_var_option { 21 | BTF_VAR_NONE = 0, 22 | BTF_VAR_PERCPU = 1, 23 | BTF_VAR_GLOBAL = 2, 24 | }; 25 | 26 | struct btf_encoder *btf_encoder__new(struct cu *cu, const char *detached_filename, struct btf *base_btf, bool verbose, struct conf_load *conf_load); 27 | void btf_encoder__delete(struct btf_encoder *encoder); 28 | 29 | int btf_encoder__encode(struct btf_encoder *encoder, struct conf_load *conf); 30 | int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu, struct conf_load *conf_load); 31 | 32 | #endif /* _BTF_ENCODER_H_ */ 33 | -------------------------------------------------------------------------------- /btfdiff: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # Copyright © 2019 Red Hat Inc, Arnaldo Carvalho de Melo 4 | # Use pahole to produce output from BTF and from DWARF, then do a diff 5 | # Use --flat_arrays with DWARF as BTF, like CTF, flattens arrays. 6 | # Use --show_private_classes as BTF shows all structs, while pahole knows 7 | # if some struct is defined only inside another struct/class or in a function, 8 | # this information is not available when loading from BTF. 9 | 10 | if [ $# -eq 0 ] ; then 11 | echo "Usage: btfdiff []" 12 | exit 1 13 | fi 14 | 15 | dwarf_input=$1 16 | btf_input=$dwarf_input 17 | 18 | if [ $# -eq 2 ] ; then 19 | btf_input=$2 20 | fi 21 | 22 | btf_output=$(mktemp /tmp/btfdiff.btf.XXXXXX) 23 | dwarf_output=$(mktemp /tmp/btfdiff.dwarf.XXXXXX) 24 | pahole_bin=${PAHOLE-"pahole"} 25 | 26 | ${pahole_bin} -E -F dwarf \ 27 | --flat_arrays \ 28 | --sort \ 29 | --jobs \ 30 | --suppress_aligned_attribute \ 31 | --suppress_force_paddings \ 32 | --suppress_packed \ 33 | --lang_exclude rust \ 34 | --show_private_classes $dwarf_input > $dwarf_output & 35 | dwarf=$! 36 | 37 | ${pahole_bin} -E -F btf \ 38 | --sort \ 39 | --suppress_aligned_attribute \ 40 | --suppress_packed \ 41 | $btf_input > $btf_output 42 | 43 | wait $dwarf 44 | 45 | diff -up $dwarf_output $btf_output 46 | 47 | rm -f $btf_output $dwarf_output 48 | exit 0 49 | -------------------------------------------------------------------------------- /buildcmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir build 4 | cd build 5 | cmake -DCMAKE_BUILD_TYPE=Release .. 6 | cd .. 7 | make -j $(getconf _NPROCESSORS_ONLN) -C build 8 | -------------------------------------------------------------------------------- /changes-v1.13: -------------------------------------------------------------------------------- 1 | Here is a summary of changes for the 1.13 version of pahole and its friends: 2 | 3 | - BTF 4 | 5 | - Use of the recently introduced BTF deduplication algorithm present in the 6 | Linux kernel's libbpf library, which allows for all the types in a multi 7 | compile unit binary such as vmlinux to be compactly stored, without duplicates. 8 | 9 | E.g.: from roughly: 10 | 11 | $ readelf -SW ../build/v5.1-rc4+/vmlinux | grep .debug_info.*PROGBITS 12 | [63] .debug_info PROGBITS 0000000000000000 1d80be0 c3c18b9 00 0 0 1 13 | $ 14 | 195 MiB 15 | 16 | to: 17 | 18 | $ time pahole --btf_encode ../build/v5.1-rc4+/vmlinux 19 | real 0m19.168s 20 | user 0m17.707s # On a Lenovo t480s (i7-8650U) SSD 21 | sys 0m1.337s 22 | $ 23 | 24 | $ readelf -SW ../build/v5.1-rc4+/vmlinux | grep .BTF.*PROGBITS 25 | [78] .BTF PROGBITS 0000000000000000 27b49f61 1e23c3 00 0 0 1 26 | $ 27 | ~2 MiB 28 | 29 | - Introduce a 'btfdiff' utility that prints the output from DWARF and from 30 | BTF, comparing the pretty printed outputs, running it on various linux 31 | kernel images, such as an allyesconfig for ppc64. 32 | 33 | Running it on the above 5.1-rc4+ vmlinux: 34 | 35 | $ btfdiff ../build/v5.1-rc4+/vmlinux 36 | $ 37 | 38 | No differences from the types generated from the DWARF ELF sections to the 39 | ones generated from the BTF ELF section. 40 | 41 | - Add a BTF loader, i.e. 'pahole -F btf' allows pretty printing of structs 42 | and unions in the same fashion as with DWARF info, and since BTF is way 43 | more compact, using it is much faster than using DWARF. 44 | 45 | $ cat ../build/v5.1-rc4+/vmlinux > /dev/null 46 | $ perf stat -e cycles pahole -F btf ../build/v5.1-rc4+/vmlinux > /dev/null 47 | 48 | Performance counter stats for 'pahole -F btf ../build/v5.1-rc4+/vmlinux': 49 | 50 | 229,712,692 cycles:u 51 | 0.063379597 seconds time elapsed 52 | 0.056265000 seconds user 53 | 0.006911000 seconds sys 54 | 55 | $ perf stat -e cycles pahole -F dwarf ../build/v5.1-rc4+/vmlinux > /dev/null 56 | 57 | Performance counter stats for 'pahole -F dwarf ../build/v5.1-rc4+/vmlinux': 58 | 59 | 49,579,679,466 cycles:u 60 | 13.063487352 seconds time elapsed 61 | 12.612512000 seconds user 62 | 0.426226000 seconds sys 63 | $ 64 | 65 | - Better union support: 66 | 67 | - Allow unions to be specified in pahole in the same fashion as structs 68 | 69 | $ pahole -C thread_union ../build/v5.1-rc4+/net/ipv4/tcp.o 70 | union thread_union { 71 | struct task_struct task __attribute__((__aligned__(64))); /* 0 11008 */ 72 | long unsigned int stack[2048]; /* 0 16384 */ 73 | }; 74 | $ 75 | 76 | - Infer __attribute__((__packed__)) when structs have no alignment holes 77 | and violate basic types (integer, longs, short integer) natural alignment 78 | requirements. Several heuristics are used to infer the __packed__ 79 | attribute, see the changeset log for descriptions. 80 | 81 | $ pahole -F btf -C boot_e820_entry ../build/v5.1-rc4+/vmlinux 82 | struct boot_e820_entry { 83 | __u64 addr; /* 0 8 */ 84 | __u64 size; /* 8 8 */ 85 | __u32 type; /* 16 4 */ 86 | 87 | /* size: 20, cachelines: 1, members: 3 */ 88 | /* last cacheline: 20 bytes */ 89 | } __attribute__((__packed__)); 90 | $ 91 | 92 | $ pahole -F btf -C lzma_header ../build/v5.1-rc4+/vmlinux 93 | struct lzma_header { 94 | uint8_t pos; /* 0 1 */ 95 | uint32_t dict_size; /* 1 4 */ 96 | uint64_t dst_size; /* 5 8 */ 97 | 98 | /* size: 13, cachelines: 1, members: 3 */ 99 | /* last cacheline: 13 bytes */ 100 | } __attribute__((__packed__)); 101 | 102 | - Support DWARF5's DW_AT_alignment, which, together with the __packed__ 103 | attribute inference algorithms produce output that, when compiled, should 104 | produce structures with layouts that match the original source code. 105 | 106 | See it in action with 'struct task_struct', which will also show some of the 107 | new information at the struct summary, at the end of the struct: 108 | 109 | $ pahole -C task_struct ../build/v5.1-rc4+/vmlinux | tail -19 110 | /* --- cacheline 103 boundary (6592 bytes) --- */ 111 | struct vm_struct * stack_vm_area; /* 6592 8 */ 112 | refcount_t stack_refcount; /* 6600 4 */ 113 | 114 | /* XXX 4 bytes hole, try to pack */ 115 | 116 | void * security; /* 6608 8 */ 117 | 118 | /* XXX 40 bytes hole, try to pack */ 119 | 120 | /* --- cacheline 104 boundary (6656 bytes) --- */ 121 | struct thread_struct thread __attribute__((__aligned__(64))); /* 6656 4352 */ 122 | 123 | /* size: 11008, cachelines: 172, members: 207 */ 124 | /* sum members: 10902, holes: 16, sum holes: 98 */ 125 | /* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */ 126 | /* paddings: 3, sum paddings: 14 */ 127 | /* forced alignments: 6, forced holes: 1, sum forced holes: 40 */ 128 | } __attribute__((__aligned__(64))); 129 | $ 130 | 131 | - Add a '--compile' option to 'pfunct' that produces compileable output for the 132 | function prototypes in an object file. There are still some bugs but the vast 133 | majority of the kernel single compilation unit files the ones produced from a 134 | single .c file are working, see the new 'fullcircle' utility that uses this 135 | feature. 136 | 137 | Example of it in action: 138 | 139 | $ pfunct --compile=static_key_false ../build/v5.1-rc4+/net/ipv4/tcp.o 140 | typedef _Bool bool; 141 | typedef struct { 142 | int counter; /* 0 4 */ 143 | 144 | /* size: 4, cachelines: 1, members: 1 */ 145 | /* last cacheline: 4 bytes */ 146 | } atomic_t; 147 | 148 | struct jump_entry; 149 | 150 | struct static_key_mod; 151 | 152 | 153 | struct static_key { 154 | atomic_t enabled; /* 0 4 */ 155 | 156 | /* XXX 4 bytes hole, try to pack */ 157 | 158 | union { 159 | long unsigned int type; /* 8 8 */ 160 | struct jump_entry * entries; /* 8 8 */ 161 | struct static_key_mod * next; /* 8 8 */ 162 | }; /* 8 8 */ 163 | 164 | /* size: 16, cachelines: 1, members: 2 */ 165 | /* sum members: 12, holes: 1, sum holes: 4 */ 166 | /* last cacheline: 16 bytes */ 167 | }; 168 | 169 | bool static_key_false(struct static_key * key) 170 | { 171 | return *(bool *)1; 172 | } 173 | 174 | $ 175 | 176 | The generation of compilable code from the type information and its use in the 177 | new tool 'fullcircle, helps validate all the parts of this codebase, finding 178 | bugs that were lurking forever, go read the csets to find all sorts of curious 179 | C language features that are rarely seen, like unnamed zero sized bitfields and 180 | the way people have been using it over the years in a codebase like the linux 181 | kernel. 182 | 183 | Certainly there are several other features, changes and fixes that I forgot to 184 | mention! Now lemme release this version so that we can use it more extensively 185 | together with a recent patch merged for 5.2: 186 | 187 | [PATCH bpf-next] kbuild: add ability to generate BTF type info for vmlinux 188 | 189 | With it BTF will be always available for all the types of the kernel, which will 190 | open a pandora box of cool new features that are in the works, and, for people 191 | already using pahole, will greatly speed up its usage. 192 | 193 | Please try to alias it to use btf, i.e. 194 | 195 | alias pahole='pahole -F btf' 196 | 197 | Please report any problems you may find with this new version or with the BTF 198 | loader or any errors in the layout generated/pretty printed. 199 | 200 | Thanks to the fine BTF guys at Facebook for the patches and help in testing, 201 | fixing bugs and getting this out of the door, the stats for this release are: 202 | 203 | Changesets: 157 204 | 205 | 113 Arnaldo Carvalho de Melo Red Hat 206 | 32 Andrii Nakryiko Facebook 207 | 10 Yonghong Song Facebook 208 | 1 Martin Lau Facebook 209 | 1 Domenico Andreoli 210 | -------------------------------------------------------------------------------- /changes-v1.16: -------------------------------------------------------------------------------- 1 | v1.16 changes: 2 | 3 | BTF encoder: 4 | 5 | Andrii Nakryiko : 6 | 7 | - Preserve and encode exported functions as BTF_KIND_FUNC. 8 | 9 | Add encoding of DWARF's DW_TAG_subprogram_type into BTF's BTF_KIND_FUNC 10 | (plus corresponding BTF_KIND_FUNC_PROTO). Only exported functions are converted 11 | for now. This allows to capture all the exported kernel functions, same subset 12 | that's exposed through /proc/kallsyms. 13 | 14 | BTF loader: 15 | 16 | Arnaldo Carvalho de Melo 17 | 18 | - Add support for BTF_KIND_FUNC 19 | 20 | Some changes to the fprintf routines were needed, as BTF has as the 21 | function type just a BTF_KIND_FUNC_PROTO, while DWARF has as the type for a 22 | function its return value type. With a function->btf flag this was overcome and 23 | all the other goodies in pfunct are present. 24 | 25 | Pretty printer: 26 | 27 | Arnaldo Carvalho de Melo: 28 | 29 | - Account inline type __aligned__ member types for spacing: 30 | 31 | union { 32 | refcount_t rcu_users; /* 2568 4 */ 33 | struct callback_head rcu __attribute__((__aligned__(8))); /* 2568 16 */ 34 | - } __attribute__((__aligned__(8))); /* 2568 16 */ 35 | + } __attribute__((__aligned__(8))); /* 2568 16 */ 36 | struct pipe_inode_info * splice_pipe; /* 2584 8 */ 37 | 38 | - Fix alignment of class members that are structs/enums/unions 39 | 40 | E.g. look at that 'completion' member in this struct: 41 | 42 | struct cpu_stop_done { 43 | atomic_t nr_todo; /* 0 4 */ 44 | int ret; /* 4 4 */ 45 | - struct completion completion; /* 8 32 */ 46 | + struct completion completion; /* 8 32 */ 47 | 48 | /* size: 40, cachelines: 1, members: 3 */ 49 | /* last cacheline: 40 bytes */ 50 | 51 | - Fixup handling classes with no members, solving a NULL deref. 52 | 53 | Gareth Lloyd : 54 | 55 | - Avoid infinite loop trying to determine type with static data member of its own type. 56 | 57 | RPM spec file. 58 | 59 | Jiri Olsa 60 | 61 | Add dwarves dependency on libdwarves1. 62 | 63 | pfunct: 64 | 65 | Arnaldo Carvalho de Melo 66 | 67 | - type->type == 0 is void, fix --compile for that 68 | 69 | We were using the fall back for that, i.e. 'return 0;' was being emitted 70 | for a function returning void, noticed with using BTF as the format. 71 | 72 | pdwtags: 73 | 74 | - Print DW_TAG_subroutine_type as well 75 | 76 | So that we can see at least via pdwtags those tags, be it from DWARF of BTF. 77 | 78 | core: 79 | 80 | Arnaldo Carvalho de Melo 81 | 82 | Fix ptr_table__add_with_id() handling of pt->nr_entries, covering how 83 | BTF variables IDs are encoded. 84 | 85 | 86 | pglobal: 87 | 88 | Arnaldo Carvalho de Melo : 89 | 90 | - Allow passing the format path specifier, to use with BTF 91 | 92 | I.e. now we can, just like with pahole, use: 93 | 94 | pglobal -F btf --variable foo.o 95 | 96 | To get the global variables. 97 | 98 | Tree wide: 99 | 100 | Arnaldo Carvalho de Melo : 101 | 102 | - Fixup issues pointed out by various coverity reports. 103 | 104 | Signed-off-by: Arnaldo Carvalho de Melo 105 | -------------------------------------------------------------------------------- /changes-v1.18: -------------------------------------------------------------------------------- 1 | v1.18: 2 | 3 | - Use type information to pretty print raw data from stdin, all 4 | documented in the man pages, further information in the csets. 5 | 6 | TLDRish: this almost completely deciphers a perf.data file: 7 | 8 | $ pahole ~/bin/perf --header=perf_file_header \ 9 | -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data 10 | 11 | What the above command line does: 12 | 13 | This will state that a 'struct perf_file_header' is available in BTF or DWARF 14 | in the ~/bin/perf file and that at the start of stdin it should be used to decode 15 | sizeof(struct perf_file_header) bytes, pretty printing it according to its members. 16 | 17 | Furthermore, that header can be referenced later in the command line, for instance 18 | that 'range=data' means that in the header, it expects a 'range' member in 19 | 'struct perf_file_header' to be used: 20 | 21 | $ pahole ~/bin/perf --header=perf_file_header < perf.data 22 | { 23 | .magic = 3622385352885552464, 24 | .size = 104, 25 | .attr_size = 136, 26 | .attrs = { 27 | .offset = 296, 28 | .size = 136, 29 | }, 30 | .data = { 31 | .offset = 432, 32 | .size = 14688, 33 | }, 34 | .event_types = { 35 | .offset = 0, 36 | .size = 0, 37 | }, 38 | .adds_features = { 376537084, 0, 0, 0 }, 39 | }, 40 | $ 41 | 42 | That 'range' field is expected to have 'offset' and 'size' fields, so that 43 | it can go on decoding a number of 'struct perf_event_header' entries. 44 | 45 | That 'sizeof' in turn expects that in 'struct perf_event_header' there is a 46 | 'size' field stating how long that particular record is, one can also use 47 | 'sizeof=some_other_member_name'. 48 | 49 | This supports variable sized records and then the 'type' field expects there 50 | is a 'struct perf_event_type' member named 'type' (again, type=something_else 51 | may be used. Finally, the value in the 'type' field is used to lookup an entry 52 | in the set formed by the two enumerations specified in the 'type_enum=' argument. 53 | 54 | If we look at these enums we'll see that its entries have names that can be, 55 | when lowercased, point to structs containing the layout for the variable sized 56 | record, which allows it to cast and produce the right pretty printed output. 57 | 58 | I.e. using the kernel BTF info we get: 59 | 60 | $ pahole perf_event_type 61 | enum perf_event_type { 62 | PERF_RECORD_MMAP = 1, 63 | PERF_RECORD_LOST = 2, 64 | PERF_RECORD_COMM = 3, 65 | PERF_RECORD_EXIT = 4, 66 | PERF_RECORD_THROTTLE = 5, 67 | PERF_RECORD_UNTHROTTLE = 6, 68 | PERF_RECORD_FORK = 7, 69 | PERF_RECORD_READ = 8, 70 | PERF_RECORD_SAMPLE = 9, 71 | PERF_RECORD_MMAP2 = 10, 72 | PERF_RECORD_AUX = 11, 73 | PERF_RECORD_ITRACE_START = 12, 74 | PERF_RECORD_LOST_SAMPLES = 13, 75 | PERF_RECORD_SWITCH = 14, 76 | PERF_RECORD_SWITCH_CPU_WIDE = 15, 77 | PERF_RECORD_NAMESPACES = 16, 78 | PERF_RECORD_KSYMBOL = 17, 79 | PERF_RECORD_BPF_EVENT = 18, 80 | PERF_RECORD_CGROUP = 19, 81 | PERF_RECORD_TEXT_POKE = 20, 82 | PERF_RECORD_MAX = 21, 83 | }; 84 | $ 85 | 86 | That is the same as in ~/bin/perf, and, if we get one of these and ask for 87 | that struct: 88 | 89 | $ pahole -C perf_record_mmap ~/bin/perf 90 | struct perf_record_mmap { 91 | struct perf_event_header header; /* 0 8 */ 92 | __u32 pid; /* 8 4 */ 93 | __u32 tid; /* 12 4 */ 94 | __u64 start; /* 16 8 */ 95 | __u64 len; /* 24 8 */ 96 | __u64 pgoff; /* 32 8 */ 97 | char filename[4096]; /* 40 4096 */ 98 | 99 | /* size: 4136, cachelines: 65, members: 7 */ 100 | /* last cacheline: 40 bytes */ 101 | }; 102 | $ 103 | 104 | Many other options were introduced to work with this, including --count, 105 | --skip, etc, look at the man page for details. 106 | 107 | - Store percpu variables in vmlinux BTF. This can be disabled when debugging 108 | kernel features being developed to use it. 109 | 110 | - pahole now should be segfault free when handling gdb test suit DWARF 111 | files, including ADA, FORTRAN, rust and dwp compressed files, the 112 | later being just flatly refused, that got left for v1.19. 113 | 114 | - Bail out on partial units for now, avoiding segfaults and providing warning 115 | to user, hopefully will be addressed in v1.19. 116 | 117 | Signed-off-by: Arnaldo Carvalho de Melo 118 | -------------------------------------------------------------------------------- /changes-v1.19: -------------------------------------------------------------------------------- 1 | v1.19: 2 | 3 | - Support split BTF, where a main BTF file, vmlinux, can be used to find types 4 | and then a kernel module, for instance, can have just what is unique to it. 5 | 6 | For instance, looking for a type in the main vmlinux BTF info: 7 | 8 | $ pahole wmi_notify_handler 9 | pahole: type 'wmi_notify_handler' not found 10 | $ 11 | 12 | If we look at the 'wmi' module BTF info that is in: 13 | 14 | $ ls -la /sys/kernel/btf/wmi 15 | -r--r--r--. 1 root root 2866 Nov 18 13:35 /sys/kernel/btf/wmi 16 | $ 17 | 18 | $ pahole /sys/kernel/btf/wmi -C wmi_notify_handler 19 | typedef void (*wmi_notify_handler)(u32, void *); 20 | $ 21 | 22 | '--btf_base=/sys/kernel/btf/vmlinux' was automatically added in this last 23 | example, an option that was also introduced in this version where types used in 24 | the wmi.ko module but present in vmlinux can be found so that there is no 25 | duplicity of types. 26 | 27 | - Update libbpf to get the split BTF support and use some of its functions to 28 | load BTF and speed up DWARF loading and BTF encoding. 29 | 30 | - Support cross-compiled ELF binaries with different endianness 31 | 32 | - Support showing typedefs for anonymous types, like structs, unions and enums, 33 | see the "Align enumerators" entry below for an example, another: 34 | 35 | $ pahole rwlock_t 36 | typedef struct { 37 | arch_rwlock_t raw_lock; /* 0 8 */ 38 | 39 | /* size: 8, cachelines: 1, members: 1 */ 40 | /* last cacheline: 8 bytes */ 41 | } rwlock_t; 42 | $ 43 | 44 | - Align enumerators: 45 | 46 | $ pahole ZSTD_strategy 47 | typedef enum { 48 | ZSTD_fast = 0, 49 | ZSTD_dfast = 1, 50 | ZSTD_greedy = 2, 51 | ZSTD_lazy = 3, 52 | ZSTD_lazy2 = 4, 53 | ZSTD_btlazy2 = 5, 54 | ZSTD_btopt = 6, 55 | ZSTD_btopt2 = 7, 56 | } ZSTD_strategy; 57 | $ 58 | 59 | - Workaround bugs in the generation of DWARF records for functions in some gcc 60 | versions that were causing breakage in the encoding of BTF: 61 | 62 | https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97060 "Missing DW_AT_declaration=1 in dwarf data" 63 | 64 | - Ignore zero-sized ELF symbols instead of erroring out. 65 | 66 | - Handle union forward declaration properly in the BTF loader. 67 | 68 | - Introduce --numeric_version for use in scripts and Makefiles: 69 | 70 | $ pahole --version 71 | v1.19 72 | $ pahole --numeric_version 73 | 119 74 | $ 75 | 76 | To avoid things like this in the kernel's scripts/link-vmlinux.sh: 77 | 78 | pahole_ver=$(${PAHOLE} --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/') 79 | 80 | - Try sole pfunct argument as a function name, just like pahole with type names: 81 | 82 | $ pfunct tcp_v4_rcv 83 | int tcp_v4_rcv(struct sk_buff * skb); 84 | $ 85 | 86 | - Speed up pfunct using some of the load techniques used in pahole. 87 | 88 | - Discard CUs after BTF encoding as they're not used anymore, greatly reducing 89 | memory usage and speeding up vmlinux BTF encoding. 90 | 91 | - Revamp how per-CPU variables are encoded in BTF. 92 | 93 | - Include BTF info for static functions. 94 | 95 | - Use BTF's string APIs for strings management, greatly improving performance 96 | over the tsearch(). 97 | 98 | - Increase size of DWARF lookup hash table, shaving off about 1 second out of 99 | about 20 seconds total for Linux BTF dedup. 100 | 101 | - Stop BTF encoding when errors are found in some DWARF CU. 102 | 103 | - Implement --packed, to show just packed structures, for instance, here are 104 | the top 5 packed data structures in the Linux kernel: 105 | 106 | $ pahole --sizes --packed | sort -k2 -nr | head -5 107 | e820_table 64004 0 108 | boot_params 4096 0 109 | efi_variable 2084 0 110 | snd_soc_tplg_pcm 912 0 111 | ntb_info_regs 800 0 112 | $ 113 | 114 | And here is one of them: 115 | 116 | $ pahole efi_variable 117 | struct efi_variable { 118 | efi_char16_t VariableName[512]; /* 0 1024 */ 119 | /* --- cacheline 16 boundary (1024 bytes) --- */ 120 | efi_guid_t VendorGuid; /* 1024 16 */ 121 | long unsigned int DataSize; /* 1040 8 */ 122 | __u8 Data[1024]; /* 1048 1024 */ 123 | /* --- cacheline 32 boundary (2048 bytes) was 24 bytes ago --- */ 124 | efi_status_t Status; /* 2072 8 */ 125 | __u32 Attributes; /* 2080 4 */ 126 | 127 | /* size: 2084, cachelines: 33, members: 6 */ 128 | /* last cacheline: 36 bytes */ 129 | } __attribute__((__packed__)); 130 | $ 131 | 132 | - Fix bug in distros such as OpenSUSE:15.2 where DW_AT_alignment isn't defined. 133 | 134 | Signed-off-by: Arnaldo Carvalho de Melo 135 | -------------------------------------------------------------------------------- /changes-v1.20: -------------------------------------------------------------------------------- 1 | BTF encoder: 2 | 3 | - Improve ELF error reporting using elf_errmsg(elf_errno()). 4 | 5 | - Improve objcopy error handling. 6 | 7 | - Fix handling of 'restrict' qualifier, that was being treated as a 'const'. 8 | 9 | - Support SHN_XINDEX in st_shndx symbol indexes, to handle ELF objects with 10 | more than 65534 sections, for instance, which happens with kernels built 11 | with 'KCFLAGS="-ffunction-sections -fdata-sections", Other cases may 12 | include when using FG-ASLR, LTO. 13 | 14 | - Cope with functions without a name, as seen sometimes when building kernel 15 | images with some versions of clang, when a SEGFAULT was taking place. 16 | 17 | - Fix BTF variable generation for kernel modules, not skipping variables at 18 | offset zero. 19 | 20 | - Fix address size to match what is in the ELF file being processed, to fix using 21 | a 64-bit pahole binary to generate BTF for a 32-bit vmlinux image. 22 | 23 | - Use kernel module ftrace addresses when finding which functions to encode, 24 | which increases the number of functions encoded. 25 | 26 | libbpf: 27 | 28 | - Allow use of packaged version, for distros wanting to dynamically link with 29 | the system's libbpf package instead of using the libbpf git submodule shipped 30 | in pahole's source code. 31 | 32 | DWARF loader: 33 | 34 | - Support DW_AT_data_bit_offset 35 | 36 | This appeared in DWARF4 but is supported only in gcc's -gdwarf-5, 37 | support it in a way that makes the output be the same for both cases. 38 | 39 | $ gcc -gdwarf-5 -c examples/dwarf5/bf.c 40 | $ pahole bf.o 41 | struct pea { 42 | long int a:1; /* 0: 0 8 */ 43 | long int b:1; /* 0: 1 8 */ 44 | long int c:1; /* 0: 2 8 */ 45 | 46 | /* XXX 29 bits hole, try to pack */ 47 | /* Bitfield combined with next fields */ 48 | 49 | int after_bitfield; /* 4 4 */ 50 | 51 | /* size: 8, cachelines: 1, members: 4 */ 52 | /* sum members: 4 */ 53 | /* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */ 54 | /* last cacheline: 8 bytes */ 55 | }; 56 | 57 | - DW_FORM_implicit_const in attr_numeric() and attr_offset() 58 | 59 | - Support DW_TAG_GNU_call_site, its the standardized rename of the previously supported 60 | DW_TAG_GNU_call_site. 61 | 62 | build: 63 | 64 | - Fix compilation on 32-bit architectures. 65 | 66 | Signed-off-by: Arnaldo Carvalho de Melo 67 | -------------------------------------------------------------------------------- /changes-v1.21: -------------------------------------------------------------------------------- 1 | DWARF loader: 2 | 3 | - Handle DWARF5 DW_OP_addrx properly 4 | 5 | Part of the effort to support the subset of DWARF5 that is generated when building the kernel. 6 | 7 | - Handle subprogram ret type with abstract_origin properly 8 | 9 | Adds a second pass to resolve abstract origin DWARF description of functions to aid 10 | the BTF encoder in getting the right return type. 11 | 12 | - Check .notes section for LTO build info 13 | 14 | When LTO is used, currently only with clang, we need to do extra steps to handle references 15 | from one object (compile unit, aka CU) to another, a way for DWARF to avoid duplicating 16 | information. 17 | 18 | - Check .debug_abbrev for cross-CU references 19 | 20 | When the kernel build process doesn't add an ELF note in vmlinux indicating that LTO was 21 | used and thus intra-CU references are present and thus we need to use a more expensive 22 | way to resolve types and (again) thus to encode BTF, we need to look at DWARF's .debug_abbrev 23 | ELF section to figure out if such intra-CU references are present. 24 | 25 | - Permit merging all DWARF CU's for clang LTO built binary 26 | 27 | Allow not trowing away previously supposedly self contained compile units 28 | (objects, aka CU, aka Compile Units) as they have type descriptions that will 29 | be used in later CUs. 30 | 31 | - Permit a flexible HASHTAGS__BITS 32 | 33 | So that we can use a more expensive algorithm when we need to keep previously processed 34 | compile units that will then be referenced by later ones to resolve types. 35 | 36 | - Use a better hashing function, from libbpf 37 | 38 | Enabling patch to combine compile units when using LTO. 39 | 40 | BTF encoder: 41 | 42 | - Add --btf_gen_all flag 43 | 44 | A new command line to allow asking for the generation of all BTF encodings, so that we 45 | can stop adding new command line options to enable new encodings in the kernel Makefile. 46 | 47 | - Match ftrace addresses within ELF functions 48 | 49 | To cope with differences in how DWARF and ftrace describes function boundaries. 50 | 51 | - Funnel ELF error reporting through a macro 52 | 53 | To use libelf's elf_error() function, improving error messages. 54 | 55 | - Sanitize non-regular int base type 56 | 57 | Cope with clang with dwarf5 non-regular int base types, tricky stuff, see yhs 58 | full explanation in the relevant cset. 59 | 60 | - Add support for the floating-point types 61 | 62 | S/390 has floats'n'doubles in its arch specific linux headers, cope with that. 63 | 64 | Pretty printer: 65 | 66 | - Honour conf_fprintf.hex when printing enumerations 67 | 68 | If the user specifies --hex in the command line, honour it when printing enumerations. 69 | 70 | Signed-off-by: Arnaldo Carvalho de Melo 71 | -------------------------------------------------------------------------------- /changes-v1.22: -------------------------------------------------------------------------------- 1 | pahole: 2 | 3 | - Allow encoding BTF to a separate BTF file (detached) instead of to a new 4 | ".BTF" ELF section in the file being encoded (vmlinux usually). 5 | 6 | - Introduce -j/--jobs option to specify the number of threads to use. Without 7 | arguments means one thread per CPU. So far used for the DWARF loader, will 8 | be used as well for the BTF encoder. 9 | 10 | - Show all different types with the same name, not just the first one found. 11 | 12 | - Introduce sorted type output (--sort), needed with multithreaded DWARF loading, 13 | to use with things like 'btfdiff' that expects the output from DWARF and BTF 14 | types to be comparable using 'diff'. 15 | 16 | - Stop assuming that reading from stdin means pretty printing as this broke 17 | pre-existing scripts, introduce a explicit --prettify command line option. 18 | 19 | - Improve type resolution for the --header command line option. 20 | 21 | - Disable incomplete CTF encoder, this needs to be done using the external 22 | libctf library. 23 | 24 | - Do not consider the ftrace filter when encoding BTF for kernel functions. 25 | 26 | - Add --kabi_prefix to avoid deduplication woes when using _RH_KABI_REPLACE(), 27 | 28 | - Add --with_flexible_array to show just types with flexible arrays. 29 | 30 | DWARF Loader: 31 | 32 | - Multithreaded loading, requires elfutils >= 0.178. 33 | 34 | - Lock calls to non-thread safe elfutils' libdw functions (dwarf_decl_file() 35 | and dwarf_decl_line()) 36 | 37 | - Change hash table size to one that performs better with current typical 38 | vmlinux files. 39 | 40 | - Allow tweaking the hash table size from the command line. 41 | 42 | - Stop allocating memory for strings obtained from libdw, just defer freeing 43 | the Dwfl handler so that references to its strings can be safely kept. 44 | 45 | - Use a frontend cache for the latest lookup result. 46 | 47 | - Allow ignoring some DWARF tags when loading for encoding BTF, as BTF doesn't 48 | have equivalents for things like DW_TAG_inline_expansion and DW_TAG_label. 49 | 50 | - Allow ignoring some DWARF tag attributes, such as DW_AT_alignment, not used 51 | when encoding BTF. 52 | 53 | - Do not query for non-C attributes when loading a C language CU (compilation unit). 54 | 55 | BTF encoder: 56 | 57 | - Preparatory work for multithreaded encoding, the focus for 1.23. 58 | 59 | btfdiff: 60 | 61 | - Support diffing against a detached BTF file, e.g.: 'btfdiff vmlinux vmlinux.btf' 62 | 63 | - Support multithreaded DWARF loading, using the new pahole --sort option to have 64 | the output from both BTF and DWARF sorted and thus comparable via 'diff'. 65 | 66 | Build: 67 | 68 | - Support building with libc libraries lacking either obstacks or argp, such 69 | as Alpine Linux's musl libc. 70 | 71 | - Support systems without getconf() to obtain the data cacheline size, such 72 | as musl libc. 73 | 74 | - Add a buildcmd.sh for test builds, tested using the same set of containers 75 | used for testing the Linux kernel perf tools. 76 | 77 | - Enable selecting building with a shared libdwarves library or statically. 78 | 79 | - Allow to use the libbpf package found in distributions instead of with the 80 | accompanying libbpf git submodule. 81 | 82 | Cleanups: 83 | 84 | - Address lots of compiler warnings accumulated by not using -Wextra, it'll 85 | be added in the next release after allowing not to use it to build libbpf. 86 | 87 | - Address covscan report issues. 88 | 89 | Documentation: 90 | 91 | - Improve the --nr_methods/-m pahole man page entry. 92 | 93 | - Clarify that currently --nr_methods doesn't work together witn -C. 94 | 95 | Signed-off-by: Arnaldo Carvalho de Melo 96 | -------------------------------------------------------------------------------- /changes-v1.23: -------------------------------------------------------------------------------- 1 | DWARF loader: 2 | 3 | - Read DW_TAG_LLVM_annotation tags, associating it with variables, functions, 4 | types. So far this is only being used by the BTF encoder, but the pretty 5 | printer should use this as well in a future release, printing these 6 | attributes when available. 7 | 8 | - Initial support for DW_TAG_skeleton_unit, so far just suggest looking up a 9 | matching .dwo file to be used instead. Automagically doing this is in the 10 | plans for a future release. 11 | 12 | - Fix heap overflow when accessing variable specification. 13 | 14 | BTF encoder: 15 | 16 | - Support the new BTF type tag attribute, encoding DW_TAG_LLVM_annotation DWARF 17 | tags as BTF_KIND_TYPE_TAG and BTF_KIND_DECL_TAG. 18 | 19 | This allows __attribute__((btf_type_tag("tag1"))) to be used for variables, 20 | functions, typedefs, so that contextual information can be stored in BTF and 21 | used by the kernel BPF verifier for more checks. 22 | 23 | The --skip_encoding_btf_type_tag option can be used to suppress this. 24 | 25 | - Fix handling of percpu symbols on s390. 26 | 27 | BTF loader: 28 | 29 | - Use cacheline size to infer alignment. 30 | 31 | btfdiff: 32 | 33 | - Now that the BTF loader infers struct member alingment, and as that is just 34 | an heuristic, suppress printing the alignment when pretty printing from BTF 35 | info like is done when printing from DWARF. 36 | 37 | pahole: 38 | 39 | - Add --skip_missing so that we don't stop when not finding one of the types passed 40 | to -C. 41 | 42 | Pretty printer: 43 | 44 | - Fix __attribute__((__aligned__(N)) printing alignment for struct members. 45 | 46 | - Fix nested __attribute__(__aligned__(N)) struct printing order, so that 47 | rebuilding from the printed source circles back to the original source code 48 | alignment semantics. 49 | 50 | Build: 51 | 52 | - No need to download libbpf source when using the system library (libbpf-devel). 53 | 54 | - Make python optional 55 | 56 | Signed-off-by: Arnaldo Carvalho de Melo 57 | -------------------------------------------------------------------------------- /changes-v1.24: -------------------------------------------------------------------------------- 1 | BTF encoder: 2 | 3 | - Add support to BTF_KIND_ENUM64 to represent enumeration entries 4 | with more than 32 bits. 5 | 6 | - Support multithreaded encoding, in addition to DWARF 7 | multithreaded loading, speeding up the process. 8 | 9 | Selected just like DWARF multithreaded loading, using the 10 | 'pahole -j' option. 11 | 12 | - Encode 'char' type as signed. 13 | 14 | BTF Loader: 15 | 16 | - Add support to BTF_KIND_ENUM64. 17 | 18 | pahole: 19 | 20 | - Introduce --lang and --lang_exclude to specify the language the 21 | DWARF compile units were originated from to use or filter. 22 | 23 | Use case is to exclude Rust compile units while aspects of the 24 | DWARF generated for it get sorted out in a way that the kernel 25 | BPF verifier don't refuse loading the BTF generated from them. 26 | 27 | - Introduce --compile to generate compilable code in a similar fashion to: 28 | 29 | bpftool btf dump file vmlinux format c > vmlinux.h 30 | 31 | As with 'bpftool', this will notice type shadowing, i.e. multiple types 32 | with the same name and will disambiguate by adding a suffix. 33 | 34 | - Don't segfault when processing bogus files. 35 | 36 | Signed-off-by: Arnaldo Carvalho de Melo 37 | -------------------------------------------------------------------------------- /changes-v1.25: -------------------------------------------------------------------------------- 1 | DWARF loader: 2 | 3 | - Support for DW_TAG_unspecified_type more generally, that in binutils 2.40 is used 4 | for assembly functions, resulting in BTF encoding problems when building the Linux 5 | kernel. 6 | 7 | - Make sure struct member offsets are in ascending order. This is part of the set of 8 | changes to support encoding BTF for Rust for use with the Linux kernel, where the 9 | BTF verifier considers invalid offset unordered struct members. 10 | 11 | - Support C atomic types (DW_TAG_atomic_type), that are not used in the Linux kernel but 12 | is present in user space components such as Open VSwitch. 13 | 14 | BTF loader: 15 | 16 | - Initial support for DW_TAG_LLVM_annotation, used for BTF type tags, to encode things 17 | like __rcu, __user annotations in the Linux kernel. This is still in flux with changes 18 | in how these are encoded that resulted from the discussion to support this in gcc in 19 | addition to in clang, where it was first designed. 20 | 21 | BTF encoder: 22 | 23 | - Exclude functions with the same name (static functions in different CUs), 24 | inconsistent prototypes or not following calling convention. 25 | 26 | - Allow generation of BTF for optimized functions, those that end with a .isra* 27 | suffix (inter procedural scalar replacement of aggregates) or .constprop* 28 | (constant propagation). 29 | 30 | Pretty printer: 31 | 32 | - For now the DW_TAG_LLVM_annotation tags are being suppressed, so the output from 33 | BTF and DWARF matches, further work is planned to support it so that the output 34 | matches the original source code and can be recompilable, resulting in the same 35 | DWARF info. 36 | 37 | - Support C atomic types, allowing the generation of source code that can be 38 | compiled with resulting DWARF info matching the original source code. 39 | 40 | pahole: 41 | 42 | - Support --lang=/--lang_exclude=asm, the DW_LANG_ define for assembly is out 43 | of order, special case it to support asking for CUs written in assembly to be 44 | selected or excluded. 45 | 46 | - Support suppressing the atomic type modifiers/attributes. 47 | 48 | - Allow filtering out functions optimized by the compiler, where the calling convention isn't 49 | the one expected by BPF or arguments are optimized out. 50 | 51 | - Support --compile from DWARF in addition to from BTF, this allows user space components 52 | such as Open VSwitch to use pahole to generate compilable code for its data structures. 53 | 54 | btfdiff: 55 | 56 | - Exclude RUST CUs, as those are not yet being BTF encoded. 57 | 58 | Signed-off-by: Arnaldo Carvalho de Melo 59 | -------------------------------------------------------------------------------- /changes-v1.26: -------------------------------------------------------------------------------- 1 | pahole: 2 | 3 | - When expanding types using 'pahole -E' do it for union and struct typedefs and for enums too. 4 | 5 | E.g: that 'state' field in 'struct module': 6 | 7 | $ pahole module | head 8 | struct module { 9 | enum module_state state; /* 0 4 */ 10 | 11 | /* XXX 4 bytes hole, try to pack */ 12 | 13 | struct list_head list; /* 8 16 */ 14 | char name[56]; /* 24 56 */ 15 | /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */ 16 | struct module_kobject mkobj; /* 80 96 */ 17 | /* --- cacheline 2 boundary (128 bytes) was 48 bytes ago --- */ 18 | $ 19 | 20 | now gets expanded: 21 | 22 | $ pahole -E module | head 23 | struct module { 24 | enum module_state { 25 | MODULE_STATE_LIVE = 0, 26 | MODULE_STATE_COMING = 1, 27 | MODULE_STATE_GOING = 2, 28 | MODULE_STATE_UNFORMED = 3, 29 | } state; /* 0 4 */ 30 | 31 | /* XXX 4 bytes hole, try to pack */ 32 | 33 | $ 34 | 35 | - Print number of holes, bit holes and bit paddings in class member types. 36 | 37 | Doing this recursively to show how much waste a complex data structure has 38 | is something that still needs to be done, there were the low hanging fruits 39 | on the path to having that feature. 40 | 41 | For instance, for 'struct task_struct' in the Linux kernel we get this 42 | extra info: 43 | 44 | --- task_struct.before.c 2024-02-09 11:38:39.249638750 -0300 45 | +++ task_struct.after.c 2024-02-09 16:19:34.221134835 -0300 46 | @@ -29,6 +29,12 @@ 47 | 48 | /* --- cacheline 2 boundary (128 bytes) --- */ 49 | struct sched_entity se; /* 128 256 */ 50 | + 51 | + /* XXX last struct has 3 holes */ 52 | + 53 | /* --- cacheline 6 boundary (384 bytes) --- */ 54 | struct sched_rt_entity rt; /* 384 48 */ 55 | struct sched_dl_entity dl; /* 432 224 */ 56 | + 57 | + /* XXX last struct has 1 bit hole */ 58 | + 59 | /* --- cacheline 10 boundary (640 bytes) was 16 bytes ago --- */ 60 | const struct sched_class * sched_class; /* 656 8 */ 61 | struct rb_node core_node; /* 664 24 */ 62 | @@ -100,6 +103,9 @@ 63 | /* --- cacheline 35 boundary (2240 bytes) was 16 bytes ago --- */ 64 | struct list_head tasks; /* 2256 16 */ 65 | struct plist_node pushable_tasks; /* 2272 40 */ 66 | + 67 | + /* XXX last struct has 1 hole */ 68 | + 69 | /* --- cacheline 36 boundary (2304 bytes) was 8 bytes ago --- */ 70 | struct rb_node pushable_dl_tasks; /* 2312 24 */ 71 | struct mm_struct * mm; /* 2336 8 */ 72 | @@ -172,6 +178,9 @@ 73 | /* XXX last struct has 4 bytes of padding */ 74 | 75 | struct vtime vtime; /* 2744 48 */ 76 | + 77 | + /* XXX last struct has 1 hole */ 78 | + 79 | /* --- cacheline 43 boundary (2752 bytes) was 40 bytes ago --- */ 80 | atomic_t tick_dep_mask; /* 2792 4 */ 81 | 82 | @@ -396,9 +405,12 @@ 83 | /* --- cacheline 145 boundary (9280 bytes) --- */ 84 | struct thread_struct thread __attribute__((__aligned__(64))); /* 9280 4416 */ 85 | 86 | + /* XXX last struct has 1 hole, 1 bit hole */ 87 | + 88 | /* size: 13696, cachelines: 214, members: 262 */ 89 | /* sum members: 13518, holes: 21, sum holes: 162 */ 90 | /* sum bitfield members: 82 bits, bit holes: 2, sum bit holes: 46 bits */ 91 | /* member types with holes: 4, total: 6, bit holes: 2, total: 2 */ 92 | /* paddings: 6, sum paddings: 49 */ 93 | /* forced alignments: 2, forced holes: 2, sum forced holes: 88 */ 94 | }; 95 | 96 | - Introduce --contains_enumerator=ENUMERATOR_NAME: 97 | 98 | E.g.: 99 | 100 | $ pahole --contains_enumerator S_VERSION 101 | enum file_time_flags { 102 | S_ATIME = 1, 103 | S_MTIME = 2, 104 | S_CTIME = 4, 105 | S_VERSION = 8, 106 | } 107 | $ 108 | 109 | The shorter form --contains_enum is also accepted. 110 | 111 | - Fix pretty printing when using DWARF, where sometimes the class (-C) and a specified "type_enum", 112 | may not be present on the same CU, so wait till both are found. 113 | 114 | Now this example that reads the 'struct perf_event_header' and 'enum perf_event_type' 115 | from the DWARF info in ~/bin/perf to pretty print records in the perf.data file works 116 | just like when using type info from BTF in ~/bin/perf: 117 | 118 | $ pahole -F dwarf -V ~/bin/perf \ 119 | --header=perf_file_header \ 120 | --seek_bytes '$header.data.offset' \ 121 | --size_bytes='$header.data.size' \ 122 | -C 'perf_event_header(sizeof,type,type_enum=perf_event_type,filter=type==PERF_RECORD_MMAP2)' \ 123 | --prettify perf.data --count 1 124 | pahole: sizeof_operator for 'perf_event_header' is 'size' 125 | pahole: type member for 'perf_event_header' is 'type' 126 | pahole: type enum for 'perf_event_header' is 'perf_event_type' 127 | pahole: filter for 'perf_event_header' is 'type==PERF_RECORD_MMAP2' 128 | pahole: seek bytes evaluated from --seek_bytes=$header.data.offset is 0x3f0 129 | pahole: size bytes evaluated from --size_bytes=$header.data.size is 0xd10 130 | // type=perf_event_header, offset=0xc20, sizeof=8, real_sizeof=112 131 | { 132 | .header = { 133 | .type = PERF_RECORD_MMAP2, 134 | .misc = 2, 135 | .size = 112, 136 | }, 137 | .pid = 1533617, 138 | .tid = 1533617, 139 | .start = 94667542700032, 140 | .len = 90112, 141 | .pgoff = 16384,{ 142 | .maj = 0, 143 | .min = 33, 144 | .ino = 35914923, 145 | .ino_generation = 26870, 146 | },{ 147 | .build_id_size = 0, 148 | .__reserved_1 = 0, 149 | .__reserved_2 = 0, 150 | .build_id = { 33, 0, 0, 0, -85, 4, 36, 2, 0, 0, 0, 0, -10, 104, 0, 0, 0, 0, 0, 0 }, 151 | }, 152 | .prot = 5, 153 | .flags = 2, 154 | .filename = "/usr/bin/ls", 155 | }, 156 | $ 157 | 158 | DWARF loader: 159 | 160 | - Add support for DW_TAG_constant, first seen in Go DWARF. 161 | 162 | - Fix loading DW_TAG_subroutine_type generated by the Go compiler, where it may 163 | have a DW_AT_byte_size. Go DWARF. And pretty print it as if 164 | it was from C, this helped in writing BPF programs to attach to Go binaries, using 165 | uprobes. 166 | 167 | BTF loader: 168 | 169 | - Fix loading of 32-bit signed enums. 170 | 171 | BTF encoder: 172 | 173 | - Add 'pahole --btf_features' to allow consumers to specify an opt-in set of 174 | features they want to use in BTF encoding. 175 | 176 | Supported features are a comma-separated combination of 177 | 178 | encode_force Ignore invalid symbols when encoding BTF. 179 | var Encode variables using BTF_KIND_VAR in BTF. 180 | float Encode floating-point types in BTF. 181 | decl_tag Encode declaration tags using BTF_KIND_DECL_TAG. 182 | type_tag Encode type tags using BTF_KIND_TYPE_TAG. 183 | enum64 Encode enum64 values with BTF_KIND_ENUM64. 184 | optimized_func Encode representations of optimized functions 185 | with suffixes like ".isra.0" etc 186 | consistent_func Avoid encoding inconsistent static functions. 187 | These occur when a parameter is optimized out 188 | in some CUs and not others, or when the same 189 | function name has inconsistent BTF descriptions 190 | in different CUs. 191 | 192 | Specifying "--btf_features=all" is the equivalent to setting all of the 193 | above. If pahole does not know about a feature specified in 194 | --btf_features it silently ignores it. 195 | 196 | The --btf_features can either be specified via a single comma-separated 197 | list 198 | --btf_features=enum64,float 199 | 200 | ...or via multiple --btf_features values 201 | 202 | --btf_features=enum64 --btf_features=float 203 | 204 | These properties allow us to use the --btf_features option in the kernel 205 | scripts/pahole_flags.sh script to specify the desired set of BTF 206 | features. 207 | 208 | If a feature named in --btf_features is not present in the version of 209 | pahole used, BTF encoding will not complain. This is desired because it 210 | means we no longer have to tie new features to a specific pahole 211 | version. 212 | 213 | Use --btf_features_strict to change that behaviour and bail out if one of 214 | the requested features isn't present. 215 | 216 | To see the supported features, use: 217 | 218 | $ pahole --supported_btf_features 219 | encode_force,var,float,decl_tag,type_tag,enum64,optimized_func,consistent_func 220 | $ 221 | 222 | btfdiff: 223 | 224 | - Parallelize loading BTF and DWARF, speeding up a bit. 225 | 226 | - Do type expansion to cover "private" types and enumerations. 227 | 228 | Signed-off-by: Arnaldo Carvalho de Melo 229 | -------------------------------------------------------------------------------- /changes-v1.27: -------------------------------------------------------------------------------- 1 | BTF encoder: 2 | 3 | - Inject kfunc decl tags into BTF from the BTF IDs ELF section in the Linux 4 | kernel vmlinux file. 5 | 6 | This allows tools such as bpftools and pfunct to enumerate the available kfuncs 7 | and to gets its function signature, the type of its return and of its 8 | arguments. See the example in the BTF loader changes description, below. 9 | 10 | - Support parallel reproducible builds, where it doesn't matter how many 11 | threads are used, the end BTF encoding result is the same. 12 | 13 | - Sanitize unsupported DWARF int type with greater-than-16 byte, as BTF doesn't 14 | support it. 15 | 16 | BTF loader: 17 | 18 | - Initial support for BTF_KIND_DECL_TAG: 19 | 20 | $ pfunct --prototypes -F btf vmlinux.btf.decl_tag,decl_tag_kfuncs | grep ^bpf_kfunc | head 21 | bpf_kfunc void cubictcp_init(struct sock * sk); 22 | bpf_kfunc void cubictcp_cwnd_event(struct sock * sk, enum tcp_ca_event event); 23 | bpf_kfunc void cubictcp_cong_avoid(struct sock * sk, u32 ack, u32 acked); 24 | bpf_kfunc u32 cubictcp_recalc_ssthresh(struct sock * sk); 25 | bpf_kfunc void cubictcp_state(struct sock * sk, u8 new_state); 26 | bpf_kfunc void cubictcp_acked(struct sock * sk, const struct ack_sample * sample); 27 | bpf_kfunc int bpf_iter_css_new(struct bpf_iter_css * it, struct cgroup_subsys_state * start, unsigned int flags); 28 | bpf_kfunc struct cgroup_subsys_state * bpf_iter_css_next(struct bpf_iter_css * it); 29 | bpf_kfunc void bpf_iter_css_destroy(struct bpf_iter_css * it); 30 | bpf_kfunc s64 bpf_map_sum_elem_count(const struct bpf_map * map); 31 | $ pfunct --prototypes -F btf vmlinux.btf.decl_tag,decl_tag_kfuncs | grep ^bpf_kfunc | wc -l 32 | 116 33 | $ 34 | 35 | pretty printing: 36 | 37 | - Fix hole discovery with inheritance in C++. 38 | 39 | Signed-off-by: Arnaldo Carvalho de Melo 40 | -------------------------------------------------------------------------------- /changes-v1.28: -------------------------------------------------------------------------------- 1 | pahole: 2 | 3 | - Various improvements to reduce the memory footprint of pahole, notably when 4 | doing BTF encoding. 5 | 6 | - Show flexible arrays statistics, it detects them at the end of member types, 7 | in the middle, etc. This should help with the efforts to spot problematic 8 | usage of flexible arrays in the kernel sources, examples: 9 | 10 | https://git.kernel.org/pub/scm/devel/pahole/pahole.git/commit/?id=6ab5318f536927cb 11 | 12 | - Introduce --with_embedded_flexible_array option. 13 | 14 | - Add '--padding N' to show only structs with N bytes of padding. 15 | 16 | - Add '--padding_ge N' to show only structs with at least N bytes of padding. 17 | 18 | - Introduce --running_kernel_vmlinux to find a vmlinux that matches the 19 | build-id of the running kernel, e.g.: 20 | 21 | $ pahole --running_kernel_vmlinux 22 | /usr/lib/debug/lib/modules/6.11.7-200.fc40.x86_64/vmlinux 23 | $ rpm -qf /usr/lib/debug/lib/modules/6.11.7-200.fc40.x86_64/vmlinux 24 | kernel-debuginfo-6.11.7-200.fc40.x86_64 25 | $ 26 | 27 | This is a shortcut to find the right vmlinux to use for the running kernel 28 | and helps with regression tests. 29 | 30 | pfunct: 31 | 32 | - Don't stop at the first function that matches a filter, show all of them. 33 | 34 | BTF Encoder: 35 | 36 | - Allow encoding data about all global variables, not just per CPU ones. 37 | 38 | There are several reasons why type information for all global variables to be 39 | useful in the kernel, including drgn without DWARF, __ksym BPF programs return 40 | type. 41 | 42 | This is non-default, experiment with it using 'pahole --btf-features=+global_var' 43 | 44 | - Handle .BTF_ids section endianness, allowing for cross builds involving 45 | machines with different endianness to work. 46 | 47 | For instance, encoding BTF info on a s390 vmlinux file on a x86_64 workstation. 48 | 49 | - Generate decl tags for bpf_fastcall for eligible kfuncs. 50 | 51 | - Add "distilled_base" BTF feature to split BTF generation. 52 | 53 | - Use the ELF_C_READ_MMAP mode with libelf, reducing peak memory utilization. 54 | 55 | BTF Loader: 56 | 57 | - Allow overiding /sys/kernel/btf/vmlinux with some other file, for testing, 58 | via the PAHOLE_VMLINUX_BTF_FILENAME environment variable. 59 | 60 | DWARF loader: 61 | 62 | - Allow setting the list of compile units produced from languages to skip via 63 | the PAHOLE_LANG_EXCLUDE environment variable. 64 | 65 | - Serialize access to elfutils dwarf_getlocation() to avoid elfutils internal 66 | data structure corruption when running multithreaded pahole. 67 | 68 | - Honour --lang_exclude when merging LTO built CUs. 69 | 70 | - Add the debuginfod client cache directory to the vmlinux search path. 71 | 72 | - Print the CU's language when a tag isn't supported. 73 | 74 | - Initial support for the DW_TAG_GNU_formal_parameter_pack, 75 | DW_TAG_GNU_template_parameter_pack, DW_TAG_template_value_param and 76 | DW_TAG_template_type_param DWARF tags. 77 | 78 | - Improve the parameter parsing by checking DW_OP_[GNU_]entry_value, this 79 | makes some more functions to be made eligible by the BTF encoder, for instance 80 | the perf_event_read() in the 6.11 kernel. 81 | 82 | Core: 83 | 84 | - Use pahole to help in reorganizing its data structures to reduce its memory 85 | footprint. 86 | 87 | Regression tests: 88 | 89 | - Introduce a tests/ directory for adding regression tests, run it with: 90 | 91 | $ tests/tests 92 | 93 | Or run the individual tests directly. 94 | 95 | - Add a regression test for the reproducible build feature that establishes 96 | as a baseline a detached BTF file without asking for a reproducible build and 97 | then compares the output of 'bpftool btf dump file' for this file with the one 98 | from BTF reproducible build encodings done with a growing number or threads. 99 | 100 | - Add a regression test for the flexible arrays features, checking if the various 101 | comments about flexible arrays match the statistics at the final of the pahole 102 | pretty print output. 103 | 104 | - Add a test that checks if pahole fails when running on a BTF system and BTF was 105 | requested, previously it was falling back to DWARF silently. 106 | 107 | - Add test validating BTF encoding, reasons we skip functions: DWARF functions 108 | that made it into BTF match signatures, functions we say we skipped, we did 109 | indeed skip them in BTF encoding and that it was correct to skip these 110 | functions. 111 | 112 | - Add regression test for 'pahole --prettify' that uses perf to record a simple 113 | workload and then pretty print the resulting perf.data file to check that what 114 | is produced are the expected records for such a file. 115 | 116 | Link: https://lore.kernel.org/all/Z0jVLcpgyENlGg6E@x1/ 117 | Tested-by: Alan Maguire 118 | Tested-by: Jiri Olsa 119 | Signed-off-by: Arnaldo Carvalho de Melo 120 | -------------------------------------------------------------------------------- /changes-v1.29: -------------------------------------------------------------------------------- 1 | DWARF loader: 2 | 3 | - Multithreading is now contained in the DWARF loader using a jobs queue and a 4 | pool of worker threads. 5 | 6 | BTF encoder: 7 | 8 | - The parallel reproducible BTF generation done using the new DWARF loader 9 | multithreading model is as fast as the old non-reproducible one and thus is 10 | now always performed, making the "reproducible_build" flag moot. 11 | 12 | The memory consumption is now greatly reduced as well. 13 | 14 | BTF loader: 15 | 16 | - Support for multiple BTF_DECL_TAGs pointing to same tag. 17 | 18 | Example: 19 | 20 | $ pfunct vmlinux -F btf -f bpf_rdonly_cast 21 | bpf_kfunc bpf_fastcall void *bpf_rdonly_cast(const void *obj__ign, u32 btf_id__k); 22 | 23 | Regression tests: 24 | 25 | - Verify that pfunct prints btf_decl_tags read from BTF. 26 | 27 | pfunct: 28 | 29 | - Don't print functions twice when using -f. 30 | 31 | Signed-off-by: Arnaldo Carvalho de Melo 32 | -------------------------------------------------------------------------------- /changes-v1.30: -------------------------------------------------------------------------------- 1 | CI testing: 2 | 3 | - support for github CI tests to build pahole with gcc 4 | and LLVM. 5 | - support for github CI tests to build pahole, a kernel 6 | along with BTF using that pahole and run tests. 7 | - tests can also be run standalone; see toplevel README 8 | for details. 9 | 10 | DWARF loader: 11 | 12 | - better detection of abort during thread processing. 13 | 14 | BTF encoder: 15 | 16 | - pahole now uses an improved scheme to detect presence of 17 | newer libbpf functions for cases where pahole is built with 18 | a non-embedded libbpf. A local weak declaration is added, 19 | and if the function is non-NULL - indicating it is present - 20 | the associated feature is avaialble. BTF feature detection 21 | makes use of this now and BTF features declared in pahole 22 | can provide a feature check function. 23 | 24 | - Type tags are now emitted for bpf_arena pointers if the 25 | attributes btf_feature is specified. 26 | 27 | - kfunc tagging has been refactored into btf_encoder__collect_kfuncs 28 | to simplify from the previous two-stage collect/tag process. 29 | 30 | - To support global variables other than per-CPU variables, code 31 | was added to match a variable with the relevant section. However 32 | variables in to-be-discarded sections have address value 0 and 33 | appeared to be in the per-CPU section (since it starts at 0). 34 | Add checks to ensure the variable really is in the relevant 35 | ELF section. 36 | 37 | - To avoid expensive variable address checking in the above case, 38 | filter out variables prefixed by __gendwarfksyms_ptr_ which are 39 | present when CONFIG_GENDWARFKSYMS is set. 40 | 41 | - Memory access bugs reported by address sanitizer were also fixed. 42 | -------------------------------------------------------------------------------- /cmake/modules/FindDWARF.cmake: -------------------------------------------------------------------------------- 1 | # - Find Dwarf 2 | # Find the dwarf.h header from elf utils 3 | # 4 | # DWARF_INCLUDE_DIR - where to find dwarf.h, etc. 5 | # DWARF_LIBRARIES - List of libraries when using elf utils. 6 | # DWARF_FOUND - True if fdo found. 7 | 8 | message(STATUS "Checking availability of DWARF and ELF development libraries") 9 | 10 | INCLUDE(CheckLibraryExists) 11 | 12 | if (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY) 13 | # Already in cache, be silent 14 | set(DWARF_FIND_QUIETLY TRUE) 15 | endif (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY) 16 | 17 | find_path(DWARF_INCLUDE_DIR dwarf.h 18 | /usr/include 19 | /usr/local/include 20 | /usr/include/libdwarf 21 | ~/usr/local/include 22 | ) 23 | 24 | find_path(LIBDW_INCLUDE_DIR elfutils/libdw.h 25 | /usr/include 26 | /usr/local/include 27 | ~/usr/local/include 28 | ) 29 | 30 | find_library(DWARF_LIBRARY 31 | NAMES dw dwarf 32 | PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 ~/usr/local/lib ~/usr/local/lib64 33 | ) 34 | 35 | find_library(ELF_LIBRARY 36 | NAMES elf 37 | PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 ~/usr/local/lib ~/usr/local/lib64 38 | ) 39 | 40 | if (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY) 41 | set(DWARF_FOUND TRUE) 42 | set(DWARF_LIBRARIES ${DWARF_LIBRARY} ${ELF_LIBRARY}) 43 | 44 | set(CMAKE_REQUIRED_LIBRARIES ${DWARF_LIBRARIES}) 45 | # check if libdw have the dwfl_module_build_id routine, i.e. if it supports the buildid 46 | # mechanism to match binaries to detached debug info sections (the -debuginfo packages 47 | # in distributions such as fedora). We do it against libelf because, IIRC, some distros 48 | # include libdw linked statically into libelf. 49 | check_library_exists(elf dwfl_module_build_id "" HAVE_DWFL_MODULE_BUILD_ID) 50 | else (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY) 51 | set(DWARF_FOUND FALSE) 52 | set(DWARF_LIBRARIES) 53 | endif (DWARF_INCLUDE_DIR AND LIBDW_INCLUDE_DIR AND DWARF_LIBRARY AND ELF_LIBRARY) 54 | 55 | if (DWARF_FOUND) 56 | if (NOT DWARF_FIND_QUIETLY) 57 | message(STATUS "Found dwarf.h header: ${DWARF_INCLUDE_DIR}") 58 | message(STATUS "Found elfutils/libdw.h header: ${LIBDW_INCLUDE_DIR}") 59 | message(STATUS "Found libdw library: ${DWARF_LIBRARY}") 60 | message(STATUS "Found libelf library: ${ELF_LIBRARY}") 61 | endif (NOT DWARF_FIND_QUIETLY) 62 | else (DWARF_FOUND) 63 | if (DWARF_FIND_REQUIRED) 64 | # Check if we are in a Red Hat (RHEL) or Fedora system to tell 65 | # exactly which packages should be installed. Please send 66 | # patches for other distributions. 67 | find_path(FEDORA fedora-release /etc) 68 | find_path(REDHAT redhat-release /etc) 69 | if (FEDORA OR REDHAT) 70 | if (NOT DWARF_INCLUDE_DIR OR NOT LIBDW_INCLUDE_DIR) 71 | message(STATUS "Please install the elfutils-devel package") 72 | endif (NOT DWARF_INCLUDE_DIR OR NOT LIBDW_INCLUDE_DIR) 73 | if (NOT DWARF_LIBRARY) 74 | message(STATUS "Please install the elfutils-libs package") 75 | endif (NOT DWARF_LIBRARY) 76 | if (NOT ELF_LIBRARY) 77 | message(STATUS "Please install the elfutils-libelf package") 78 | endif (NOT ELF_LIBRARY) 79 | else (FEDORA OR REDHAT) 80 | if (NOT DWARF_INCLUDE_DIR) 81 | message(STATUS "Could NOT find dwarf include dir") 82 | endif (NOT DWARF_INCLUDE_DIR) 83 | if (NOT LIBDW_INCLUDE_DIR) 84 | message(STATUS "Could NOT find libdw include dir") 85 | endif (NOT LIBDW_INCLUDE_DIR) 86 | if (NOT DWARF_LIBRARY) 87 | message(STATUS "Could NOT find libdw library") 88 | endif (NOT DWARF_LIBRARY) 89 | if (NOT ELF_LIBRARY) 90 | message(STATUS "Could NOT find libelf library") 91 | endif (NOT ELF_LIBRARY) 92 | endif (FEDORA OR REDHAT) 93 | message(FATAL_ERROR "Could NOT find some ELF and DWARF libraries, please install the missing packages") 94 | endif (DWARF_FIND_REQUIRED) 95 | endif (DWARF_FOUND) 96 | 97 | mark_as_advanced(DWARF_INCLUDE_DIR LIBDW_INCLUDE_DIR DWARF_LIBRARY ELF_LIBRARY) 98 | include_directories(${DWARF_INCLUDE_DIR} ${LIBDW_INCLUDE_DIR}) 99 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/config.h) 100 | 101 | message(STATUS "Checking availability of DWARF and ELF development libraries - done") 102 | -------------------------------------------------------------------------------- /cmake/modules/Findargp.cmake: -------------------------------------------------------------------------------- 1 | # - Find argp 2 | # Figure out if argp is in glibc or if it argp-standalone 3 | # 4 | # ARGP_LIBRARY - Library to use argp 5 | # ARGP_FOUND - True if found. 6 | 7 | message(STATUS "Checking availability of argp library") 8 | 9 | INCLUDE(CheckLibraryExists) 10 | 11 | if (ARGP_LIBRARY) 12 | # Already in cache, be silent 13 | set(ARGP_FIND_QUIETLY TRUE) 14 | endif (ARGP_LIBRARY) 15 | 16 | find_library(ARGP_LIBRARY 17 | NAMES argp 18 | PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 ~/usr/local/lib ~/usr/local/lib64 19 | ) 20 | 21 | if (ARGP_LIBRARY) 22 | set(ARGP_FOUND TRUE) 23 | set(ARGP_LIBRARY ${ARGP_LIBRARY}) 24 | set(CMAKE_REQUIRED_LIBRARIES ${ARGP_LIBRARY}) 25 | else (ARGP_LIBRARY) 26 | set(ARGP_LIBRARY "") 27 | endif (ARGP_LIBRARY) 28 | 29 | if (ARGP_FOUND) 30 | if (NOT ARGP_FIND_QUIETLY) 31 | message(STATUS "Found argp library: ${ARGP_LIBRARY}") 32 | endif (NOT ARGP_FIND_QUIETLY) 33 | else (ARGP_FOUND) 34 | set(ARGP_FOUND TRUE) 35 | message(STATUS "Assuming argp is in libc") 36 | endif (ARGP_FOUND) 37 | 38 | mark_as_advanced(ARGP_LIBRARY) 39 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/config.h) 40 | 41 | message(STATUS "Checking availability of argp library - done") 42 | -------------------------------------------------------------------------------- /cmake/modules/Findobstack.cmake: -------------------------------------------------------------------------------- 1 | # - Find obstack 2 | # Figure out if obstack is in glibc or if it musl-obstack or elsewhere 3 | # 4 | # OBSTACK_LIBRARY - Library to use obstack 5 | # OBSTACK_FOUND - True if found. 6 | 7 | message(STATUS "Checking availability of obstack library") 8 | 9 | INCLUDE(CheckLibraryExists) 10 | 11 | if (OBSTACK_LIBRARY) 12 | # Already in cache, be silent 13 | set(OBSTACK_FIND_QUIETLY TRUE) 14 | endif (OBSTACK_LIBRARY) 15 | 16 | find_library(OBSTACK_LIBRARY 17 | NAMES obstack 18 | PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 ~/usr/local/lib ~/usr/local/lib64 19 | ) 20 | 21 | if (OBSTACK_LIBRARY) 22 | set(OBSTACK_FOUND TRUE) 23 | set(OBSTACK_LIBRARY ${OBSTACK_LIBRARY}) 24 | set(CMAKE_REQUIRED_LIBRARIES ${OBSTACK_LIBRARY}) 25 | else (OBSTACK_LIBRARY) 26 | set(OBSTACK_LIBRARY "") 27 | endif (OBSTACK_LIBRARY) 28 | 29 | if (OBSTACK_FOUND) 30 | if (NOT OBSTACK_FIND_QUIETLY) 31 | message(STATUS "Found obstack library: ${OBSTACK_LIBRARY}") 32 | endif (NOT OBSTACK_FIND_QUIETLY) 33 | else (OBSTACK_FOUND) 34 | set(OBSTACK_FOUND TRUE) 35 | message(STATUS "Assuming obstack is in libc") 36 | endif (OBSTACK_FOUND) 37 | 38 | mark_as_advanced(OBSTACK_LIBRARY) 39 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/config.h) 40 | 41 | message(STATUS "Checking availability of obstack library - done") 42 | -------------------------------------------------------------------------------- /config.h.cmake: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007 Arnaldo Carvalho de Melo 3 | 4 | This program is free software; you can redistribute it and/or modify it 5 | under the terms of version 2 of the GNU General Public License as 6 | published by the Free Software Foundation. 7 | */ 8 | 9 | #cmakedefine HAVE_DWFL_MODULE_BUILD_ID 10 | -------------------------------------------------------------------------------- /ctf.h: -------------------------------------------------------------------------------- 1 | #ifndef _CTF_H 2 | #define _CTF_H 3 | /* 4 | SPDX-License-Identifier: GPL-2.0-only 5 | 6 | Copyright (C) 2019 Arnaldo Carvalho de Melo 7 | */ 8 | 9 | #include 10 | 11 | struct ctf_header { 12 | uint16_t ctf_magic; /* Header magic value */ 13 | #define CTF_MAGIC 0xcff1 14 | #define CTF_MAGIC_SWAP 0xf1cf 15 | 16 | uint8_t ctf_version; /* Header version */ 17 | #define CTF_VERSION 2 18 | 19 | uint8_t ctf_flags; /* Header flags */ 20 | #define CTF_FLAGS_COMPR 0x01 21 | 22 | uint32_t ctf_parent_label; /* Label of parent CTF object */ 23 | uint32_t ctf_parent_name; /* Name of parent CTF object */ 24 | 25 | /* All offsets are in bytes are relative to the end of 26 | * this header. 27 | */ 28 | uint32_t ctf_label_off; /* Offset of label section */ 29 | uint32_t ctf_object_off; /* Offset of data object section */ 30 | uint32_t ctf_func_off; /* Offset of function section */ 31 | uint32_t ctf_type_off; /* Offset of type section */ 32 | uint32_t ctf_str_off; /* Offset of string section */ 33 | uint32_t ctf_str_len; /* Length of string section */ 34 | }; 35 | 36 | #define CTF_REF_OFFSET(REF) ((REF) & 0x7fffffff) 37 | #define CTF_REF_TBL_ID(REF) (((REF) >> 31) & 0x1) 38 | #define CTF_STR_TBL_ID_0 0 39 | #define CTF_STR_TBL_ID_1 1 40 | 41 | #define CTF_REF_ENCODE(TBL, OFF) (((TBL) << 31) | (OFF)) 42 | 43 | struct ctf_label_ent { 44 | uint32_t ctf_label_ref; 45 | uint32_t ctf_type_index; 46 | }; 47 | 48 | /* Types are encoded with ctf_short_type so long as the ctf_size 49 | * field can be fully represented in a uint16_t. If not, then 50 | * the ctf_size is given the value 0xffff and ctf_full_type is 51 | * used. 52 | */ 53 | struct ctf_short_type { 54 | uint32_t ctf_name; 55 | uint16_t ctf_info; 56 | union { 57 | uint16_t ctf_size; 58 | uint16_t ctf_type; 59 | }; 60 | }; 61 | 62 | struct ctf_full_type { 63 | struct ctf_short_type base; 64 | uint32_t ctf_size_high; 65 | uint32_t ctf_size_low; 66 | }; 67 | 68 | #define CTF_GET_KIND(VAL) (((VAL) >> 11) & 0x1f) 69 | #define CTF_GET_VLEN(VAL) ((VAL) & 0x3ff) 70 | #define CTF_ISROOT(VAL) (((VAL) & 0x400) != 0) 71 | 72 | #define CTF_INFO_ENCODE(KIND, VLEN, ISROOT) \ 73 | (((ISROOT) ? 0x400 : 0) | ((KIND) << 11) | (VLEN)) 74 | 75 | #define CTF_TYPE_KIND_UNKN 0 /* Unknown */ 76 | #define CTF_TYPE_KIND_INT 1 /* Integer */ 77 | #define CTF_TYPE_KIND_FLT 2 /* Float */ 78 | #define CTF_TYPE_KIND_PTR 3 /* Pointer */ 79 | #define CTF_TYPE_KIND_ARR 4 /* Array */ 80 | #define CTF_TYPE_KIND_FUNC 5 /* Function */ 81 | #define CTF_TYPE_KIND_STR 6 /* Struct */ 82 | #define CTF_TYPE_KIND_UNION 7 /* Union */ 83 | #define CTF_TYPE_KIND_ENUM 8 /* Enumeration */ 84 | #define CTF_TYPE_KIND_FWD 9 /* Forward */ 85 | #define CTF_TYPE_KIND_TYPDEF 10 /* Typedef */ 86 | #define CTF_TYPE_KIND_VOLATILE 11 /* Volatile */ 87 | #define CTF_TYPE_KIND_CONST 12 /* Const */ 88 | #define CTF_TYPE_KIND_RESTRICT 13 /* Restrict */ 89 | #define CTF_TYPE_KIND_MAX 31 90 | 91 | #define CTF_TYPE_INT_ATTRS(VAL) ((VAL) >> 24) 92 | #define CTF_TYPE_INT_OFFSET(VAL) (((VAL) >> 16) & 0xff) 93 | #define CTF_TYPE_INT_BITS(VAL) ((VAL) & 0xffff) 94 | 95 | #define CTF_TYPE_INT_ENCODE(ATTRS, OFF, BITS) \ 96 | (((ATTRS) << 24) | ((OFF) << 16) | (BITS)) 97 | 98 | /* Integer type attributes */ 99 | #define CTF_TYPE_INT_SIGNED 0x1 100 | #define CTF_TYPE_INT_CHAR 0x2 101 | #define CTF_TYPE_INT_BOOL 0x4 102 | #define CTF_TYPE_INT_VARARGS 0x8 103 | 104 | #define CTF_TYPE_FP_ATTRS(VAL) ((VAL) >> 24) 105 | #define CTF_TYPE_FP_OFFSET(VAL) (((VAL) >> 16) & 0xff) 106 | #define CTF_TYPE_FP_BITS(VAL) ((VAL) & 0xffff) 107 | 108 | #define CTF_TYPE_FP_ENCODE(ATTRS, OFF, BITS) \ 109 | (((ATTRS) << 24) | ((OFF) << 16) | (BITS)) 110 | 111 | /* Possible values for the float type attribute field */ 112 | #define CTF_TYPE_FP_SINGLE 1 113 | #define CTF_TYPE_FP_DOUBLE 2 114 | #define CTF_TYPE_FP_CMPLX 3 115 | #define CTF_TYPE_FP_CMPLX_DBL 4 116 | #define CTF_TYPE_FP_CMPLX_LDBL 5 117 | #define CTF_TYPE_FP_LDBL 6 118 | #define CTF_TYPE_FP_INTVL 7 119 | #define CTF_TYPE_FP_INTVL_DBL 8 120 | #define CTF_TYPE_FP_INTVL_LDBL 9 121 | #define CTF_TYPE_FP_IMGRY 10 122 | #define CTF_TYPE_FP_IMGRY_DBL 11 123 | #define CTF_TYPE_FP_IMGRY_LDBL 12 124 | #define CTF_TYPE_FP_MAX 12 125 | 126 | struct ctf_enum { 127 | uint32_t ctf_enum_name; 128 | uint32_t ctf_enum_val; 129 | }; 130 | 131 | struct ctf_array { 132 | uint16_t ctf_array_type; 133 | uint16_t ctf_array_index_type; 134 | uint32_t ctf_array_nelems; 135 | }; 136 | 137 | /* Struct members are encoded with either ctf_short_member or 138 | * ctf_full_member, depending upon the 'size' of the struct or 139 | * union being defined. If it is less than CTF_SHORT_MEMBER_LIMIT 140 | * then ctf_short_member objects are used to encode, else 141 | * ctf_full_member is used. 142 | */ 143 | #define CTF_SHORT_MEMBER_LIMIT 8192 144 | 145 | struct ctf_short_member { 146 | uint32_t ctf_member_name; 147 | uint16_t ctf_member_type; 148 | uint16_t ctf_member_offset; 149 | }; 150 | 151 | struct ctf_full_member { 152 | uint32_t ctf_member_name; 153 | uint16_t ctf_member_type; 154 | uint16_t ctf_member_unused; 155 | uint32_t ctf_member_offset_high; 156 | uint32_t ctf_member_offset_low; 157 | }; 158 | 159 | #endif /* _CTF_H */ 160 | -------------------------------------------------------------------------------- /ctf_encoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-only 3 | 4 | Copyright (C) 2009 Red Hat Inc. 5 | Copyright (C) 2009 Arnaldo Carvalho de Melo 6 | */ 7 | 8 | #include "dwarves.h" 9 | #include "libctf.h" 10 | #include "ctf.h" 11 | #include "hash.h" 12 | #include "elf_symtab.h" 13 | #include 14 | 15 | static int tag__check_id_drift(const struct tag *tag, 16 | uint32_t core_id, uint32_t ctf_id) 17 | { 18 | if (ctf_id != core_id) { 19 | fprintf(stderr, "%s: %s id drift, core: %u, libctf: %d\n", 20 | __func__, dwarf_tag_name(tag->tag), core_id, ctf_id); 21 | return -1; 22 | } 23 | return 0; 24 | } 25 | 26 | static int dwarf_to_ctf_type(uint16_t tag) 27 | { 28 | switch (tag) { 29 | case DW_TAG_const_type: return CTF_TYPE_KIND_CONST; 30 | case DW_TAG_pointer_type: return CTF_TYPE_KIND_PTR; 31 | case DW_TAG_restrict_type: return CTF_TYPE_KIND_RESTRICT; 32 | case DW_TAG_volatile_type: return CTF_TYPE_KIND_VOLATILE; 33 | case DW_TAG_class_type: 34 | case DW_TAG_structure_type: return CTF_TYPE_KIND_STR; 35 | case DW_TAG_union_type: return CTF_TYPE_KIND_UNION; 36 | } 37 | return 0xffff; 38 | } 39 | 40 | static int base_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf) 41 | { 42 | struct base_type *bt = tag__base_type(tag); 43 | uint32_t ctf_id = ctf__add_base_type(ctf, bt->name, bt->bit_size); 44 | 45 | if (tag__check_id_drift(tag, core_id, ctf_id)) 46 | return -1; 47 | 48 | return 0; 49 | } 50 | 51 | static int pointer_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf) 52 | { 53 | uint32_t ctf_id = ctf__add_short_type(ctf, dwarf_to_ctf_type(tag->tag), tag->type, 0); 54 | 55 | if (tag__check_id_drift(tag, core_id, ctf_id)) 56 | return -1; 57 | 58 | return 0; 59 | } 60 | 61 | static int typedef__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf) 62 | { 63 | uint32_t ctf_id = ctf__add_short_type(ctf, CTF_TYPE_KIND_TYPDEF, tag->type, tag__namespace(tag)->name); 64 | 65 | if (tag__check_id_drift(tag, core_id, ctf_id)) 66 | return -1; 67 | 68 | return 0; 69 | } 70 | 71 | static int fwd_decl__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf) 72 | { 73 | uint32_t ctf_id = ctf__add_fwd_decl(ctf, tag__namespace(tag)->name); 74 | 75 | if (tag__check_id_drift(tag, core_id, ctf_id)) 76 | return -1; 77 | 78 | return 0; 79 | } 80 | 81 | static int structure_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf) 82 | { 83 | struct type *type = tag__type(tag); 84 | int64_t position; 85 | uint32_t ctf_id = ctf__add_struct(ctf, dwarf_to_ctf_type(tag->tag), 86 | type->namespace.name, type->size, 87 | type->nr_members, &position); 88 | 89 | if (tag__check_id_drift(tag, core_id, ctf_id)) 90 | return -1; 91 | 92 | const bool is_short = type->size < CTF_SHORT_MEMBER_LIMIT; 93 | struct class_member *pos; 94 | type__for_each_data_member(type, pos) { 95 | if (is_short) 96 | ctf__add_short_member(ctf, pos->name, pos->tag.type, 97 | pos->bit_offset, &position); 98 | else 99 | ctf__add_full_member(ctf, pos->name, pos->tag.type, 100 | pos->bit_offset, &position); 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | static uint32_t array_type__nelems(struct tag *tag) 107 | { 108 | int i; 109 | uint32_t nelem = 1; 110 | struct array_type *array = tag__array_type(tag); 111 | 112 | for (i = array->dimensions - 1; i >= 0; --i) 113 | nelem *= array->nr_entries[i]; 114 | 115 | return nelem; 116 | } 117 | 118 | static int array_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf) 119 | { 120 | const uint32_t nelems = array_type__nelems(tag); 121 | uint32_t ctf_id = ctf__add_array(ctf, tag->type, 0, nelems); 122 | 123 | if (tag__check_id_drift(tag, core_id, ctf_id)) 124 | return -1; 125 | 126 | return 0; 127 | } 128 | 129 | static int subroutine_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf) 130 | { 131 | struct parameter *pos; 132 | int64_t position; 133 | struct ftype *ftype = tag__ftype(tag); 134 | uint32_t ctf_id = ctf__add_function_type(ctf, tag->type, ftype->nr_parms, ftype->unspec_parms, &position); 135 | 136 | if (tag__check_id_drift(tag, core_id, ctf_id)) 137 | return -1; 138 | 139 | ftype__for_each_parameter(ftype, pos) 140 | ctf__add_parameter(ctf, pos->tag.type, &position); 141 | 142 | return 0; 143 | } 144 | 145 | static int enumeration_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf) 146 | { 147 | struct type *etype = tag__type(tag); 148 | int64_t position; 149 | uint32_t ctf_id = ctf__add_enumeration_type(ctf, etype->namespace.name, 150 | etype->size, etype->nr_members, 151 | &position); 152 | 153 | if (tag__check_id_drift(tag, core_id, ctf_id)) 154 | return -1; 155 | 156 | struct enumerator *pos; 157 | type__for_each_enumerator(etype, pos) 158 | ctf__add_enumerator(ctf, pos->name, pos->value, &position); 159 | 160 | return 0; 161 | } 162 | 163 | static void tag__encode_ctf(struct tag *tag, uint32_t core_id, struct ctf *ctf) 164 | { 165 | switch (tag->tag) { 166 | case DW_TAG_base_type: 167 | base_type__encode(tag, core_id, ctf); 168 | break; 169 | case DW_TAG_const_type: 170 | case DW_TAG_pointer_type: 171 | case DW_TAG_restrict_type: 172 | case DW_TAG_volatile_type: 173 | pointer_type__encode(tag, core_id, ctf); 174 | break; 175 | case DW_TAG_typedef: 176 | typedef__encode(tag, core_id, ctf); 177 | break; 178 | case DW_TAG_structure_type: 179 | case DW_TAG_union_type: 180 | case DW_TAG_class_type: 181 | if (tag__type(tag)->declaration) 182 | fwd_decl__encode(tag, core_id, ctf); 183 | else 184 | structure_type__encode(tag, core_id, ctf); 185 | break; 186 | case DW_TAG_array_type: 187 | array_type__encode(tag, core_id, ctf); 188 | break; 189 | case DW_TAG_subroutine_type: 190 | subroutine_type__encode(tag, core_id, ctf); 191 | break; 192 | case DW_TAG_enumeration_type: 193 | enumeration_type__encode(tag, core_id, ctf); 194 | break; 195 | } 196 | } 197 | 198 | #define HASHADDR__BITS 8 199 | #define HASHADDR__SIZE (1UL << HASHADDR__BITS) 200 | #define hashaddr__fn(key) hash_64(key, HASHADDR__BITS) 201 | 202 | static struct function *hashaddr__find_function(const struct hlist_head hashtable[], 203 | const uint64_t addr) 204 | { 205 | struct function *function; 206 | struct hlist_node *pos; 207 | uint16_t bucket = hashaddr__fn(addr); 208 | const struct hlist_head *head = &hashtable[bucket]; 209 | 210 | hlist_for_each_entry(function, pos, head, tool_hnode) { 211 | if (function->lexblock.ip.addr == addr) 212 | return function; 213 | } 214 | 215 | return NULL; 216 | } 217 | 218 | static struct variable *hashaddr__find_variable(const struct hlist_head hashtable[], 219 | const uint64_t addr) 220 | { 221 | struct variable *variable; 222 | struct hlist_node *pos; 223 | uint16_t bucket = hashaddr__fn(addr); 224 | const struct hlist_head *head = &hashtable[bucket]; 225 | 226 | hlist_for_each_entry(variable, pos, head, tool_hnode) { 227 | if (variable->ip.addr == addr) 228 | return variable; 229 | } 230 | 231 | return NULL; 232 | } 233 | 234 | /* 235 | * FIXME: Its in the DWARF loader, we have to find a better handoff 236 | * mechanizm... 237 | */ 238 | extern struct strings *strings; 239 | 240 | int cu__encode_ctf(struct cu *cu, int verbose) 241 | { 242 | int err = -1; 243 | struct ctf *ctf = ctf__new(cu->filename, cu->elf); 244 | 245 | if (ctf == NULL) 246 | goto out; 247 | 248 | if (cu__cache_symtab(cu) < 0) 249 | goto out_delete; 250 | 251 | ctf__set_strings(ctf, strings); 252 | 253 | uint32_t id; 254 | struct tag *pos; 255 | cu__for_each_type(cu, id, pos) 256 | tag__encode_ctf(pos, id, ctf); 257 | 258 | struct hlist_head hash_addr[HASHADDR__SIZE]; 259 | 260 | for (id = 0; id < HASHADDR__SIZE; ++id) 261 | INIT_HLIST_HEAD(&hash_addr[id]); 262 | 263 | struct function *function; 264 | cu__for_each_function(cu, id, function) { 265 | uint64_t addr = function->lexblock.ip.addr; 266 | struct hlist_head *head = &hash_addr[hashaddr__fn(addr)]; 267 | hlist_add_head(&function->tool_hnode, head); 268 | } 269 | 270 | uint64_t addr; 271 | GElf_Sym sym; 272 | const char *sym_name; 273 | cu__for_each_cached_symtab_entry(cu, id, sym, sym_name) { 274 | if (ctf__ignore_symtab_function(&sym, sym_name)) 275 | continue; 276 | 277 | addr = elf_sym__value(&sym); 278 | int64_t position; 279 | function = hashaddr__find_function(hash_addr, addr); 280 | if (function == NULL) { 281 | if (verbose) 282 | fprintf(stderr, 283 | "function %4d: %-20s %#" PRIx64 " %5u NOT FOUND!\n", 284 | id, sym_name, addr, 285 | elf_sym__size(&sym)); 286 | err = ctf__add_function(ctf, 0, 0, 0, &position); 287 | if (err != 0) 288 | goto out_err_ctf; 289 | continue; 290 | } 291 | 292 | const struct ftype *ftype = &function->proto; 293 | err = ctf__add_function(ctf, function->proto.tag.type, 294 | ftype->nr_parms, 295 | ftype->unspec_parms, &position); 296 | 297 | if (err != 0) 298 | goto out_err_ctf; 299 | 300 | struct parameter *pos; 301 | ftype__for_each_parameter(ftype, pos) 302 | ctf__add_function_parameter(ctf, pos->tag.type, &position); 303 | } 304 | 305 | for (id = 0; id < HASHADDR__SIZE; ++id) 306 | INIT_HLIST_HEAD(&hash_addr[id]); 307 | 308 | struct variable *var; 309 | cu__for_each_variable(cu, id, pos) { 310 | var = tag__variable(pos); 311 | if (variable__scope(var) != VSCOPE_GLOBAL) 312 | continue; 313 | struct hlist_head *head = &hash_addr[hashaddr__fn(var->ip.addr)]; 314 | hlist_add_head(&var->tool_hnode, head); 315 | } 316 | 317 | cu__for_each_cached_symtab_entry(cu, id, sym, sym_name) { 318 | if (ctf__ignore_symtab_object(&sym, sym_name)) 319 | continue; 320 | addr = elf_sym__value(&sym); 321 | 322 | var = hashaddr__find_variable(hash_addr, addr); 323 | if (var == NULL) { 324 | if (verbose) 325 | fprintf(stderr, 326 | "variable %4d: %-20s %#" PRIx64 " %5u NOT FOUND!\n", 327 | id, sym_name, addr, 328 | elf_sym__size(&sym)); 329 | err = ctf__add_object(ctf, 0); 330 | if (err != 0) 331 | goto out_err_ctf; 332 | continue; 333 | } 334 | 335 | err = ctf__add_object(ctf, var->ip.tag.type); 336 | if (err != 0) 337 | goto out_err_ctf; 338 | } 339 | 340 | ctf__encode(ctf, CTF_FLAGS_COMPR); 341 | 342 | err = 0; 343 | out_delete: 344 | ctf__delete(ctf); 345 | out: 346 | return err; 347 | out_err_ctf: 348 | fprintf(stderr, 349 | "%4d: %-20s %#llx %5u failed encoding, " 350 | "ABORTING!\n", id, sym_name, 351 | (unsigned long long)addr, elf_sym__size(&sym)); 352 | goto out_delete; 353 | } 354 | -------------------------------------------------------------------------------- /ctf_encoder.h: -------------------------------------------------------------------------------- 1 | #ifndef _CTF_ENCODER_H_ 2 | #define _CTF_ENCODER_H_ 1 3 | /* 4 | SPDX-License-Identifier: GPL-2.0-only 5 | 6 | Copyright (C) 2009 Red Hat Inc. 7 | Copyright (C) 2009 Arnaldo Carvalho de Melo 8 | */ 9 | 10 | struct cu; 11 | 12 | int cu__encode_ctf(struct cu *cu, int verbose); 13 | 14 | #endif /* _CTF_ENCODER_H_ */ 15 | -------------------------------------------------------------------------------- /ctfdwdiff: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | results_dir=/tmp/ctfdwdiff 4 | 5 | diff_tool() { 6 | local tool=$1 7 | local dwarf_options=$2 8 | local ctf_options=$3 9 | local obj=$4 10 | 11 | diff=$results_dir/$obj.$tool.diff 12 | ctf=$results_dir/$obj.$tool.ctf.c 13 | dwarf=$results_dir/$obj.$tool.dwarf.c 14 | $tool -F ctf $ctf_options $obj > $ctf 15 | $tool -F dwarf $dwarf_options $obj > $dwarf 16 | diff -up $dwarf $ctf > $diff 17 | if [ -s $diff ] ; then 18 | [ $# -gt 4 ] && vim $diff 19 | exit 0 20 | else 21 | rm -f $diff $ctf $dwarf 22 | fi 23 | } 24 | 25 | diff_one() { 26 | local obj=$1 27 | diff_tool "pahole" "--flat_arrays --show_private_classes --fixup_silly_bitfields" " " $obj $2 28 | diff_tool "pfunct" "-V --no_parm_names" "-V" $obj $2 29 | } 30 | 31 | 32 | diff_dir() { 33 | find . -type d | \ 34 | while read dir ; do 35 | cd $dir 36 | ls *.o 2> /dev/null | 37 | while read obj ; do 38 | ncus=$(readelf -wi $obj | grep DW_TAG_compile_unit | wc -l) 39 | if [ $ncus -ne 1 ] ; then 40 | continue 41 | fi 42 | echo $obj 43 | pahole -Z $obj 44 | diff_one $obj $1 45 | done 46 | cd - > /dev/null 47 | done 48 | } 49 | 50 | rm -rf $results_dir 51 | mkdir $results_dir 52 | 53 | if [ $# -lt 2 ] ; then 54 | diff_dir 55 | else 56 | diff_one $* 57 | fi 58 | -------------------------------------------------------------------------------- /dtagnames.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-only 3 | 4 | Copyright (C) 2006 Mandriva Conectiva S.A. 5 | Copyright (C) 2006 Arnaldo Carvalho de Melo 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include "dwarves.h" 12 | #include "dutil.h" 13 | 14 | static struct conf_fprintf conf; 15 | 16 | static struct conf_load conf_load = { 17 | .conf_fprintf = &conf, 18 | }; 19 | 20 | static int class__tag_name(struct tag *tag, struct cu *cu __maybe_unused, 21 | void *cookie __maybe_unused) 22 | { 23 | puts(dwarf_tag_name(tag->tag)); 24 | return 0; 25 | } 26 | 27 | static int cu__dump_class_tag_names(struct cu *cu, void *cookie __maybe_unused) 28 | { 29 | cu__for_all_tags(cu, class__tag_name, NULL); 30 | return 0; 31 | } 32 | 33 | static void cus__dump_class_tag_names(struct cus *cus) 34 | { 35 | cus__for_each_cu(cus, cu__dump_class_tag_names, NULL, NULL); 36 | } 37 | 38 | int main(int argc __maybe_unused, char *argv[]) 39 | { 40 | int err, rc = EXIT_FAILURE; 41 | struct cus *cus = cus__new(); 42 | 43 | if (dwarves__init() || cus == NULL) { 44 | fputs("dtagnames: insufficient memory\n", stderr); 45 | goto out; 46 | } 47 | dwarves__resolve_cacheline_size(NULL, 0); 48 | 49 | err = cus__load_files(cus, &conf_load, argv + 1); 50 | if (err != 0) { 51 | cus__fprintf_load_files_err(cus, "dtagnames", argv + 1, err, stderr); 52 | goto out; 53 | } 54 | 55 | cus__dump_class_tag_names(cus); 56 | rc = EXIT_SUCCESS; 57 | out: 58 | cus__delete(cus); 59 | dwarves__exit(); 60 | return rc; 61 | } 62 | -------------------------------------------------------------------------------- /dutil.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-only 3 | 4 | Copyright (C) 2007 Arnaldo Carvalho de Melo 5 | */ 6 | 7 | 8 | #include "dutil.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | void *zalloc(size_t size) 17 | { 18 | return calloc(1, size); 19 | } 20 | 21 | void __zfree(void **ptr) 22 | { 23 | free(*ptr); 24 | *ptr = NULL; 25 | } 26 | 27 | struct str_node *str_node__new(const char *s, bool dupstr) 28 | { 29 | struct str_node *snode = malloc(sizeof(*snode)); 30 | 31 | if (snode != NULL){ 32 | if (dupstr) { 33 | s = strdup(s); 34 | if (s == NULL) 35 | goto out_delete; 36 | } 37 | snode->s = s; 38 | } 39 | 40 | return snode; 41 | 42 | out_delete: 43 | free(snode); 44 | return NULL; 45 | } 46 | 47 | static void str_node__delete(struct str_node *snode, bool dupstr) 48 | { 49 | if (snode == NULL) 50 | return; 51 | 52 | if (dupstr) 53 | zfree(&snode->s); 54 | free(snode); 55 | } 56 | 57 | int __strlist__add(struct strlist *slist, const char *new_entry, void *priv) 58 | { 59 | struct rb_node **p = &slist->entries.rb_node; 60 | struct rb_node *parent = NULL; 61 | struct str_node *sn; 62 | 63 | while (*p != NULL) { 64 | int rc; 65 | 66 | parent = *p; 67 | sn = rb_entry(parent, struct str_node, rb_node); 68 | rc = strcmp(sn->s, new_entry); 69 | 70 | if (rc > 0) 71 | p = &(*p)->rb_left; 72 | else if (rc < 0) 73 | p = &(*p)->rb_right; 74 | else 75 | return -EEXIST; 76 | } 77 | 78 | sn = str_node__new(new_entry, slist->dupstr); 79 | if (sn == NULL) 80 | return -ENOMEM; 81 | 82 | rb_link_node(&sn->rb_node, parent, p); 83 | rb_insert_color(&sn->rb_node, &slist->entries); 84 | 85 | sn->priv = priv; 86 | 87 | list_add_tail(&sn->node, &slist->list_entries); 88 | 89 | return 0; 90 | } 91 | 92 | int strlist__add(struct strlist *slist, const char *new_entry) 93 | { 94 | return __strlist__add(slist, new_entry, NULL); 95 | } 96 | 97 | int strlist__load(struct strlist *slist, const char *filename) 98 | { 99 | char entry[1024]; 100 | int err = -1; 101 | FILE *fp = fopen(filename, "r"); 102 | 103 | if (fp == NULL) 104 | return -1; 105 | 106 | while (fgets(entry, sizeof(entry), fp) != NULL) { 107 | const size_t len = strlen(entry); 108 | 109 | if (len == 0) 110 | continue; 111 | entry[len - 1] = '\0'; 112 | 113 | if (strlist__add(slist, entry) != 0) 114 | goto out; 115 | } 116 | 117 | err = 0; 118 | out: 119 | fclose(fp); 120 | return err; 121 | } 122 | 123 | struct strlist *strlist__new(bool dupstr) 124 | { 125 | struct strlist *slist = malloc(sizeof(*slist)); 126 | 127 | if (slist != NULL) { 128 | slist->entries = RB_ROOT; 129 | INIT_LIST_HEAD(&slist->list_entries); 130 | slist->dupstr = dupstr; 131 | } 132 | 133 | return slist; 134 | } 135 | 136 | void strlist__delete(struct strlist *slist) 137 | { 138 | if (slist != NULL) { 139 | struct str_node *pos; 140 | struct rb_node *next = rb_first(&slist->entries); 141 | 142 | while (next) { 143 | pos = rb_entry(next, struct str_node, rb_node); 144 | next = rb_next(&pos->rb_node); 145 | strlist__remove(slist, pos); 146 | } 147 | slist->entries = RB_ROOT; 148 | free(slist); 149 | } 150 | } 151 | 152 | void strlist__remove(struct strlist *slist, struct str_node *sn) 153 | { 154 | rb_erase(&sn->rb_node, &slist->entries); 155 | list_del_init(&sn->node); 156 | str_node__delete(sn, slist->dupstr); 157 | } 158 | 159 | bool strlist__has_entry(struct strlist *slist, const char *entry) 160 | { 161 | struct rb_node **p = &slist->entries.rb_node; 162 | struct rb_node *parent = NULL; 163 | 164 | while (*p != NULL) { 165 | struct str_node *sn; 166 | int rc; 167 | 168 | parent = *p; 169 | sn = rb_entry(parent, struct str_node, rb_node); 170 | rc = strcmp(sn->s, entry); 171 | 172 | if (rc > 0) 173 | p = &(*p)->rb_left; 174 | else if (rc < 0) 175 | p = &(*p)->rb_right; 176 | else 177 | return true; 178 | } 179 | 180 | return false; 181 | } 182 | 183 | Elf_Scn *elf_section_by_name(Elf *elf, GElf_Shdr *shp, const char *name, size_t *index) 184 | { 185 | Elf_Scn *sec = NULL; 186 | size_t cnt = 1; 187 | size_t str_idx; 188 | 189 | if (elf_getshdrstrndx(elf, &str_idx)) 190 | return NULL; 191 | 192 | while ((sec = elf_nextscn(elf, sec)) != NULL) { 193 | char *str; 194 | 195 | gelf_getshdr(sec, shp); 196 | str = elf_strptr(elf, str_idx, shp->sh_name); 197 | if (!str) 198 | return NULL; 199 | if (!strcmp(name, str)) { 200 | if (index) 201 | *index = cnt; 202 | break; 203 | } 204 | ++cnt; 205 | } 206 | 207 | return sec; 208 | } 209 | 210 | Elf_Scn *elf_section_by_idx(Elf *elf, GElf_Shdr *shp, int idx, const char **name_out) 211 | { 212 | Elf_Scn *sec; 213 | size_t str_idx; 214 | 215 | sec = elf_getscn(elf, idx); 216 | if (!sec) 217 | return NULL; 218 | if (!gelf_getshdr(sec, shp)) 219 | return NULL; 220 | if (name_out) { 221 | if (elf_getshdrstrndx(elf, &str_idx)) 222 | return NULL; 223 | *name_out = elf_strptr(elf, str_idx, shp->sh_name); 224 | } 225 | return sec; 226 | } 227 | 228 | char *strlwr(char *s) 229 | { 230 | int len = strlen(s), i; 231 | 232 | for (i = 0; i < len; ++i) 233 | s[i] = tolower(s[i]); 234 | 235 | return s; 236 | } 237 | -------------------------------------------------------------------------------- /dwarves_emit.h: -------------------------------------------------------------------------------- 1 | #ifndef _DWARVES_EMIT_H_ 2 | #define _DWARVES_EMIT_H_ 1 3 | /* 4 | SPDX-License-Identifier: GPL-2.0-only 5 | 6 | Copyright (C) 2006 Mandriva Conectiva S.A. 7 | Copyright (C) 2006 Arnaldo Carvalho de Melo 8 | Copyright (C) 2007 Arnaldo Carvalho de Melo 9 | */ 10 | 11 | #include 12 | #include 13 | #include "list.h" 14 | 15 | struct cu; 16 | struct ftype; 17 | struct tag; 18 | struct type; 19 | struct conf_fprintf; 20 | 21 | struct type_emissions { 22 | struct list_head definitions; /* struct type entries */ 23 | struct list_head base_type_definitions; /* struct base_type entries */ 24 | struct list_head fwd_decls; /* struct class entries */ 25 | struct conf_fprintf *conf_fprintf; 26 | }; 27 | 28 | void type_emissions__init(struct type_emissions *temissions, struct conf_fprintf *conf_fprintf); 29 | 30 | int ftype__emit_definitions(struct ftype *ftype, struct cu *cu, 31 | struct type_emissions *emissions, FILE *fp); 32 | int type__emit_definitions(struct tag *tag, struct cu *cu, 33 | struct type_emissions *emissions, FILE *fp); 34 | void type__emit(struct tag *tag_type, struct cu *cu, 35 | const char *prefix, const char *suffix, FILE *fp); 36 | struct type *type_emissions__find_definition(const struct type_emissions *temissions, 37 | uint16_t tag, const char *name); 38 | 39 | #endif /* _DWARVES_EMIT_H_ */ 40 | -------------------------------------------------------------------------------- /dwarves_reorganize.h: -------------------------------------------------------------------------------- 1 | #ifndef _DWARVES_REORGANIZE_H_ 2 | #define _DWARVES_REORGANIZE_H_ 1 3 | /* 4 | SPDX-License-Identifier: GPL-2.0-only 5 | 6 | Copyright (C) 2006 Mandriva Conectiva S.A. 7 | Copyright (C) 2006 Arnaldo Carvalho de Melo 8 | Copyright (C) 2007 Arnaldo Carvalho de Melo 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | 15 | struct class; 16 | struct cu; 17 | struct class_member; 18 | 19 | void class__subtract_offsets_from(struct class *cls, struct class_member *from, 20 | const uint16_t size); 21 | 22 | void class__add_offsets_from(struct class *cls, struct class_member *from, 23 | const uint16_t size); 24 | 25 | void class__fixup_alignment(struct class *cls, const struct cu *cu); 26 | 27 | void class__reorganize(struct class *cls, const struct cu *cu, 28 | const int verbose, FILE *fp); 29 | 30 | #endif /* _DWARVES_REORGANIZE_H_ */ 31 | -------------------------------------------------------------------------------- /elf_symtab.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-only 3 | 4 | Copyright (C) 2009 Red Hat Inc. 5 | Copyright (C) 2009 Arnaldo Carvalho de Melo 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "dutil.h" 13 | #include "elf_symtab.h" 14 | 15 | #define HASHSYMS__BITS 8 16 | #define HASHSYMS__SIZE (1UL << HASHSYMS__BITS) 17 | 18 | struct elf_symtab *elf_symtab__new(const char *name, Elf *elf) 19 | { 20 | size_t symtab_index; 21 | 22 | if (name == NULL) 23 | name = ".symtab"; 24 | 25 | GElf_Shdr shdr; 26 | Elf_Scn *sec = elf_section_by_name(elf, &shdr, name, &symtab_index); 27 | 28 | if (sec == NULL) 29 | return NULL; 30 | 31 | if (gelf_getshdr(sec, &shdr) == NULL) 32 | return NULL; 33 | 34 | struct elf_symtab *symtab = zalloc(sizeof(*symtab)); 35 | if (symtab == NULL) 36 | return NULL; 37 | 38 | symtab->name = strdup(name); 39 | if (symtab->name == NULL) 40 | goto out_delete; 41 | 42 | symtab->syms = elf_getdata(sec, NULL); 43 | if (symtab->syms == NULL) 44 | goto out_free_name; 45 | 46 | /* 47 | * This returns extended section index table's 48 | * section index, if it exists. 49 | */ 50 | int symtab_xindex = elf_scnshndx(sec); 51 | 52 | sec = elf_getscn(elf, shdr.sh_link); 53 | if (sec == NULL) 54 | goto out_free_name; 55 | 56 | symtab->symstrs = elf_getdata(sec, NULL); 57 | if (symtab->symstrs == NULL) 58 | goto out_free_name; 59 | 60 | /* 61 | * The .symtab section has optional extended section index 62 | * table, load its data so it can be used to resolve symbol's 63 | * section index. 64 | **/ 65 | if (symtab_xindex > 0) { 66 | GElf_Shdr shdr_xindex; 67 | Elf_Scn *sec_xindex; 68 | 69 | sec_xindex = elf_getscn(elf, symtab_xindex); 70 | if (sec_xindex == NULL) 71 | goto out_free_name; 72 | 73 | if (gelf_getshdr(sec_xindex, &shdr_xindex) == NULL) 74 | goto out_free_name; 75 | 76 | /* Extra check to verify it's correct type */ 77 | if (shdr_xindex.sh_type != SHT_SYMTAB_SHNDX) 78 | goto out_free_name; 79 | 80 | /* Extra check to verify it belongs to the .symtab */ 81 | if (symtab_index != shdr_xindex.sh_link) 82 | goto out_free_name; 83 | 84 | symtab->syms_sec_idx_table = elf_getdata(elf_getscn(elf, symtab_xindex), NULL); 85 | if (symtab->syms_sec_idx_table == NULL) 86 | goto out_free_name; 87 | } 88 | 89 | symtab->nr_syms = shdr.sh_size / shdr.sh_entsize; 90 | 91 | return symtab; 92 | out_free_name: 93 | zfree(&symtab->name); 94 | out_delete: 95 | free(symtab); 96 | return NULL; 97 | } 98 | 99 | void elf_symtab__delete(struct elf_symtab *symtab) 100 | { 101 | if (symtab == NULL) 102 | return; 103 | zfree(&symtab->name); 104 | free(symtab); 105 | } 106 | -------------------------------------------------------------------------------- /elf_symtab.h: -------------------------------------------------------------------------------- 1 | #ifndef _ELF_SYMTAB_H_ 2 | #define _ELF_SYMTAB_H_ 1 3 | /* 4 | SPDX-License-Identifier: GPL-2.0-only 5 | 6 | Copyright (C) 2009 Red Hat Inc. 7 | Copyright (C) 2009 Arnaldo Carvalho de Melo 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | struct elf_symtab { 16 | uint32_t nr_syms; 17 | Elf_Data *syms; 18 | Elf_Data *symstrs; 19 | /* Data of SHT_SYMTAB_SHNDX section. */ 20 | Elf_Data *syms_sec_idx_table; 21 | char *name; 22 | }; 23 | 24 | struct elf_symtab *elf_symtab__new(const char *name, Elf *elf); 25 | void elf_symtab__delete(struct elf_symtab *symtab); 26 | 27 | static inline uint32_t elf_symtab__nr_symbols(const struct elf_symtab *symtab) 28 | { 29 | return symtab->nr_syms; 30 | } 31 | 32 | static inline const char *elf_sym__name(const GElf_Sym *sym, 33 | const struct elf_symtab *symtab) 34 | { 35 | return symtab->symstrs->d_buf + sym->st_name; 36 | } 37 | 38 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) 39 | { 40 | return GELF_ST_TYPE(sym->st_info); 41 | } 42 | 43 | static inline uint16_t elf_sym__section(const GElf_Sym *sym) 44 | { 45 | return sym->st_shndx; 46 | } 47 | 48 | static inline uint8_t elf_sym__bind(const GElf_Sym *sym) 49 | { 50 | return GELF_ST_BIND(sym->st_info); 51 | } 52 | 53 | static inline uint8_t elf_sym__visibility(const GElf_Sym *sym) 54 | { 55 | return GELF_ST_VISIBILITY(sym->st_other); 56 | } 57 | 58 | static inline uint32_t elf_sym__size(const GElf_Sym *sym) 59 | { 60 | return sym->st_size; 61 | } 62 | 63 | static inline uint64_t elf_sym__value(const GElf_Sym *sym) 64 | { 65 | return sym->st_value; 66 | } 67 | 68 | static inline bool elf_sym__is_local_function(const GElf_Sym *sym) 69 | { 70 | return elf_sym__type(sym) == STT_FUNC && 71 | sym->st_name != 0 && 72 | sym->st_shndx != SHN_UNDEF; 73 | } 74 | 75 | static inline bool elf_sym__is_local_object(const GElf_Sym *sym) 76 | { 77 | return elf_sym__type(sym) == STT_OBJECT && 78 | sym->st_name != 0 && 79 | sym->st_shndx != SHN_UNDEF; 80 | } 81 | 82 | static inline bool 83 | elf_sym__get(Elf_Data *syms, Elf_Data *syms_sec_idx_table, 84 | int id, GElf_Sym *sym, Elf32_Word *sym_sec_idx) 85 | { 86 | if (!gelf_getsymshndx(syms, syms_sec_idx_table, id, sym, sym_sec_idx)) 87 | return false; 88 | 89 | if (sym->st_shndx != SHN_XINDEX) 90 | *sym_sec_idx = sym->st_shndx; 91 | 92 | return true; 93 | } 94 | 95 | /** 96 | * elf_symtab__for_each_symbol - iterate thru all the symbols 97 | * 98 | * @symtab: struct elf_symtab instance to iterate 99 | * @index: uint32_t index 100 | * @sym: GElf_Sym iterator 101 | */ 102 | #define elf_symtab__for_each_symbol(symtab, index, sym) \ 103 | for (index = 0, gelf_getsym(symtab->syms, index, &sym);\ 104 | index < symtab->nr_syms; \ 105 | index++, gelf_getsym(symtab->syms, index, &sym)) 106 | 107 | /** 108 | * elf_symtab__for_each_symbol_index - iterate through all the symbols, 109 | * that takes extended symbols indexes into account 110 | * 111 | * @symtab: struct elf_symtab instance to iterate 112 | * @index: uint32_t index 113 | * @sym: GElf_Sym iterator 114 | * @sym_sec_idx: symbol's index 115 | */ 116 | #define elf_symtab__for_each_symbol_index(symtab, id, sym, sym_sec_idx) \ 117 | for (id = 0; id < symtab->nr_syms; id++) \ 118 | if (elf_sym__get(symtab->syms, symtab->syms_sec_idx_table, \ 119 | id, &sym, &sym_sec_idx)) 120 | 121 | #endif /* _ELF_SYMTAB_H_ */ 122 | -------------------------------------------------------------------------------- /elfcreator.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0-only 3 | * 4 | * Copyright 2009 Red Hat, Inc. 5 | * 6 | * Author: Peter Jones 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "elfcreator.h" 19 | 20 | struct elf_creator { 21 | const char *path; 22 | int fd; 23 | 24 | Elf *elf; 25 | GElf_Ehdr *ehdr, ehdr_mem; 26 | 27 | Elf *oldelf; 28 | /* just because we have to look this up /so/ often... */ 29 | Elf_Scn *dynscn; 30 | GElf_Shdr *dynshdr, dynshdr_mem; 31 | Elf_Data *dyndata; 32 | }; 33 | 34 | static void clear(ElfCreator *ctor, int do_unlink) 35 | { 36 | if (do_unlink) { 37 | if (ctor->elf) 38 | elf_end(ctor->elf); 39 | if (ctor->fd >= 0) 40 | close(ctor->fd); 41 | if (ctor->path) 42 | unlink(ctor->path); 43 | } else { 44 | if (ctor->elf) { 45 | elf_update(ctor->elf, ELF_C_WRITE_MMAP); 46 | elf_end(ctor->elf); 47 | } 48 | if (ctor->fd >= 0) 49 | close(ctor->fd); 50 | } 51 | memset(ctor, '\0', sizeof(*ctor)); 52 | } 53 | 54 | ElfCreator *elfcreator_begin(char *path, Elf *elf) { 55 | ElfCreator *ctor = NULL; 56 | GElf_Ehdr ehdr_mem, *ehdr; 57 | 58 | if (!(ctor = calloc(1, sizeof(*ctor)))) 59 | return NULL; 60 | 61 | clear(ctor, 0); 62 | 63 | ctor->path = path; 64 | ctor->oldelf = elf; 65 | 66 | ehdr = gelf_getehdr(elf, &ehdr_mem); 67 | 68 | if ((ctor->fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0755)) < 0) { 69 | err: 70 | clear(ctor, 1); 71 | free(ctor); 72 | return NULL; 73 | } 74 | 75 | if (!(ctor->elf = elf_begin(ctor->fd, ELF_C_WRITE_MMAP, elf))) 76 | goto err; 77 | 78 | gelf_newehdr(ctor->elf, gelf_getclass(elf)); 79 | gelf_update_ehdr(ctor->elf, ehdr); 80 | 81 | if (!(ctor->ehdr = gelf_getehdr(ctor->elf, &ctor->ehdr_mem))) 82 | goto err; 83 | 84 | return ctor; 85 | } 86 | 87 | static Elf_Scn *get_scn_by_type(ElfCreator *ctor, Elf64_Word sh_type) 88 | { 89 | Elf_Scn *scn = NULL; 90 | 91 | while ((scn = elf_nextscn(ctor->elf, scn)) != NULL) { 92 | GElf_Shdr *shdr, shdr_mem; 93 | 94 | shdr = gelf_getshdr(scn, &shdr_mem); 95 | if (shdr->sh_type == sh_type) 96 | return scn; 97 | } 98 | return NULL; 99 | } 100 | 101 | static void update_dyn_cache(ElfCreator *ctor) 102 | { 103 | ctor->dynscn = get_scn_by_type(ctor, SHT_DYNAMIC); 104 | if (ctor->dynscn == NULL) 105 | return; 106 | 107 | ctor->dynshdr = gelf_getshdr(ctor->dynscn, &ctor->dynshdr_mem); 108 | ctor->dyndata = elf_getdata(ctor->dynscn, NULL); 109 | } 110 | 111 | void elfcreator_copy_scn(ElfCreator *ctor, Elf_Scn *scn) 112 | { 113 | Elf_Scn *newscn; 114 | Elf_Data *indata, *outdata; 115 | GElf_Shdr *oldshdr, oldshdr_mem; 116 | GElf_Shdr *newshdr, newshdr_mem; 117 | 118 | newscn = elf_newscn(ctor->elf); 119 | newshdr = gelf_getshdr(newscn, &newshdr_mem); 120 | 121 | oldshdr = gelf_getshdr(scn, &oldshdr_mem); 122 | 123 | memmove(newshdr, oldshdr, sizeof(*newshdr)); 124 | gelf_update_shdr(newscn, newshdr); 125 | 126 | indata = NULL; 127 | while ((indata = elf_getdata(scn, indata)) != NULL) { 128 | outdata = elf_newdata(newscn); 129 | *outdata = *indata; 130 | } 131 | if (newshdr->sh_type == SHT_DYNAMIC) 132 | update_dyn_cache(ctor); 133 | } 134 | 135 | static GElf_Dyn *get_dyn_by_tag(ElfCreator *ctor, Elf64_Sxword d_tag, 136 | GElf_Dyn *mem, size_t *idx) 137 | { 138 | size_t cnt; 139 | 140 | if (!ctor->dyndata) 141 | return NULL; 142 | 143 | for (cnt = 1; cnt < ctor->dynshdr->sh_size / ctor->dynshdr->sh_entsize; 144 | cnt++) { 145 | GElf_Dyn *dyn; 146 | 147 | if ((dyn = gelf_getdyn(ctor->dyndata, cnt, mem)) == NULL) 148 | break; 149 | 150 | if (dyn->d_tag == d_tag) { 151 | *idx = cnt; 152 | return dyn; 153 | } 154 | } 155 | return NULL; 156 | } 157 | 158 | static void remove_dyn(ElfCreator *ctor, size_t idx) 159 | { 160 | size_t cnt; 161 | 162 | for (cnt = idx; cnt < ctor->dynshdr->sh_size/ctor->dynshdr->sh_entsize; 163 | cnt++) { 164 | GElf_Dyn *dyn, dyn_mem; 165 | 166 | if (cnt+1 == ctor->dynshdr->sh_size/ctor->dynshdr->sh_entsize) { 167 | memset(&dyn_mem, '\0', sizeof(dyn_mem)); 168 | gelf_update_dyn(ctor->dyndata, cnt, &dyn_mem); 169 | break; 170 | } 171 | 172 | dyn = gelf_getdyn(ctor->dyndata, cnt+1, &dyn_mem); 173 | gelf_update_dyn(ctor->dyndata, cnt, dyn); 174 | } 175 | ctor->dynshdr->sh_size--; 176 | gelf_update_shdr(ctor->dynscn, ctor->dynshdr); 177 | update_dyn_cache(ctor); 178 | } 179 | 180 | typedef void (*dyn_fixup_fn)(ElfCreator *ctor, Elf64_Sxword d_tag, Elf_Scn *scn); 181 | 182 | static void generic_dyn_fixup_fn(ElfCreator *ctor, Elf64_Sxword d_tag, Elf_Scn *scn) 183 | { 184 | GElf_Shdr *shdr, shdr_mem; 185 | GElf_Dyn *dyn, dyn_mem; 186 | size_t idx = 0; 187 | 188 | dyn = get_dyn_by_tag(ctor, d_tag, &dyn_mem, &idx); 189 | shdr = gelf_getshdr(scn, &shdr_mem); 190 | if (shdr) { 191 | dyn->d_un.d_ptr = shdr->sh_addr; 192 | gelf_update_dyn(ctor->dyndata, idx, dyn); 193 | } else { 194 | remove_dyn(ctor, idx); 195 | } 196 | } 197 | 198 | static void rela_dyn_fixup_fn(ElfCreator *ctor, Elf64_Sxword d_tag, Elf_Scn *scn) 199 | { 200 | GElf_Shdr *shdr, shdr_mem; 201 | GElf_Dyn *dyn, dyn_mem; 202 | size_t idx = 0; 203 | 204 | dyn = get_dyn_by_tag(ctor, d_tag, &dyn_mem, &idx); 205 | shdr = gelf_getshdr(scn, &shdr_mem); 206 | if (shdr) { 207 | dyn->d_un.d_ptr = shdr->sh_addr; 208 | gelf_update_dyn(ctor->dyndata, idx, dyn); 209 | } else { 210 | remove_dyn(ctor, idx); 211 | dyn = get_dyn_by_tag(ctor, DT_RELASZ, &dyn_mem, &idx); 212 | if (dyn) { 213 | dyn->d_un.d_val = 0; 214 | gelf_update_dyn(ctor->dyndata, idx, dyn); 215 | } 216 | } 217 | } 218 | 219 | static void rel_dyn_fixup_fn(ElfCreator *ctor, Elf64_Sxword d_tag, Elf_Scn *scn) 220 | { 221 | GElf_Shdr *shdr, shdr_mem; 222 | GElf_Dyn *dyn, dyn_mem; 223 | size_t idx = 0; 224 | 225 | dyn = get_dyn_by_tag(ctor, d_tag, &dyn_mem, &idx); 226 | shdr = gelf_getshdr(scn, &shdr_mem); 227 | if (shdr) { 228 | dyn->d_un.d_ptr = shdr->sh_addr; 229 | gelf_update_dyn(ctor->dyndata, idx, dyn); 230 | } else { 231 | remove_dyn(ctor, idx); 232 | dyn = get_dyn_by_tag(ctor, DT_RELSZ, &dyn_mem, &idx); 233 | if (dyn) { 234 | dyn->d_un.d_val = 0; 235 | gelf_update_dyn(ctor->dyndata, idx, dyn); 236 | } 237 | } 238 | } 239 | 240 | static void fixup_dynamic(ElfCreator *ctor) 241 | { 242 | struct { 243 | Elf64_Sxword d_tag; 244 | Elf64_Word sh_type; 245 | dyn_fixup_fn fn; 246 | } fixups[] = { 247 | { DT_HASH, SHT_HASH, NULL }, 248 | { DT_STRTAB, SHT_STRTAB, NULL }, 249 | { DT_SYMTAB, SHT_SYMTAB, NULL }, 250 | { DT_RELA, SHT_RELA, rela_dyn_fixup_fn}, 251 | { DT_REL, SHT_REL, rel_dyn_fixup_fn}, 252 | { DT_GNU_HASH, SHT_GNU_HASH, NULL }, 253 | { DT_NULL, SHT_NULL, NULL } 254 | }; 255 | int i; 256 | 257 | for (i = 0; fixups[i].d_tag != DT_NULL; i++) { 258 | Elf_Scn *scn; 259 | 260 | scn = get_scn_by_type(ctor, fixups[i].sh_type); 261 | if (fixups[i].fn) 262 | fixups[i].fn(ctor, fixups[i].d_tag, scn); 263 | else 264 | generic_dyn_fixup_fn(ctor, fixups[i].d_tag, scn); 265 | } 266 | } 267 | 268 | void elfcreator_end(ElfCreator *ctor) 269 | { 270 | GElf_Phdr phdr_mem, *phdr; 271 | int m,n; 272 | 273 | for (m = 0; (phdr = gelf_getphdr(ctor->oldelf, m, &phdr_mem)) != NULL; m++) 274 | /* XXX this should check if an entry is needed */; 275 | 276 | gelf_newphdr(ctor->elf, m); 277 | elf_update(ctor->elf, ELF_C_NULL); 278 | update_dyn_cache(ctor); 279 | 280 | for (n = 0; n < m; n++) { 281 | /* XXX this should check if an entry is needed */ 282 | phdr = gelf_getphdr(ctor->oldelf, n, &phdr_mem); 283 | if (ctor->dynshdr && phdr->p_type == PT_DYNAMIC) 284 | phdr->p_offset = ctor->dynshdr->sh_offset; 285 | 286 | gelf_update_phdr(ctor->elf, n, phdr); 287 | } 288 | 289 | fixup_dynamic(ctor); 290 | 291 | clear(ctor, 0); 292 | free(ctor); 293 | } 294 | -------------------------------------------------------------------------------- /elfcreator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0-only 3 | * 4 | * Copyright 2009 Red Hat, Inc. 5 | * 6 | * Author: Peter Jones 7 | */ 8 | #ifndef ELFCREATOR_H 9 | #define ELFCREATOR_H 1 10 | 11 | #include 12 | 13 | typedef struct elf_creator ElfCreator; 14 | extern ElfCreator *elfcreator_begin(char *path, Elf *elf); 15 | extern void elfcreator_copy_scn(ElfCreator *ctor, Elf_Scn *scn); 16 | extern void elfcreator_end(ElfCreator *ctor); 17 | 18 | #endif /* ELFCREATOR_H */ 19 | -------------------------------------------------------------------------------- /fullcircle: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # Copyright © 2019 Red Hat Inc, Arnaldo Carvalho de Melo 4 | # Use pfunct to produce compilable output from a object, then do a codiff -s 5 | # To see if the type information generated from source code generated 6 | # from type information in a file compiled from the original source code matches. 7 | 8 | if [ $# -eq 0 ] ; then 9 | echo "Usage: fullcircle " 10 | exit 1 11 | fi 12 | 13 | file=$1 14 | 15 | nr_cus=$(readelf -wi ${file} | grep DW_TAG_compile_unit | wc -l) 16 | if [ $nr_cus -gt 1 ]; then 17 | exit 0 18 | fi 19 | 20 | c_output=$(mktemp /tmp/fullcircle.XXXXXX.c) 21 | o_output=$(mktemp /tmp/fullcircle.XXXXXX.o) 22 | pfunct_bin=${PFUNCT-"pfunct"} 23 | codiff_bin=${CODIFF-"codiff"} 24 | 25 | # See how your DW_AT_producer looks like and find the 26 | # right regexp to get after the GCC version string, this one 27 | # seems good enough for Red Hat/Fedora/CentOS that look like: 28 | # 29 | # DW_AT_producer : (indirect string, offset: 0x3583): GNU C89 8.2.1 20181215 (Red Hat 8.2.1-6) -mno-sse -mno-mmx 30 | # 31 | # So we need from -mno-sse onwards 32 | 33 | CFLAGS=$(readelf -wi $file | grep -w DW_AT_producer | sed -r 's/.*\)( -[[:alnum:]]+.*)+/\1/g') 34 | 35 | # Check if we managed to do the sed or if this is something like GNU AS 36 | [ "${CFLAGS/DW_AT_producer/}" != "${CFLAGS}" ] && exit 37 | 38 | ${pfunct_bin} --compile $file > $c_output 39 | gcc $CFLAGS -c -g $c_output -o $o_output 40 | ${codiff_bin} -q -s $file $o_output 41 | 42 | rm -f $c_output $o_output 43 | exit 0 44 | -------------------------------------------------------------------------------- /gobuffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-only 3 | 4 | Copyright (C) 2008 Arnaldo Carvalho de Melo 5 | 6 | Grow only buffer, add entries but never delete 7 | */ 8 | 9 | #include "gobuffer.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "dutil.h" 20 | 21 | #define GOBUFFER__BCHUNK (8 * 1024) 22 | #define GOBUFFER__ZCHUNK (8 * 1024) 23 | 24 | void gobuffer__init(struct gobuffer *gb) 25 | { 26 | gb->entries = NULL; 27 | gb->nr_entries = gb->allocated_size = 0; 28 | /* 0 == NULL */ 29 | gb->index = 1; 30 | } 31 | 32 | struct gobuffer *gobuffer__new(void) 33 | { 34 | struct gobuffer *gb = malloc(sizeof(*gb)); 35 | 36 | if (gb != NULL) 37 | gobuffer__init(gb); 38 | 39 | return gb; 40 | } 41 | 42 | void __gobuffer__delete(struct gobuffer *gb) 43 | { 44 | if (gb == NULL) 45 | return; 46 | 47 | zfree(&gb->entries); 48 | } 49 | 50 | void gobuffer__delete(struct gobuffer *gb) 51 | { 52 | __gobuffer__delete(gb); 53 | free(gb); 54 | } 55 | 56 | void *gobuffer__ptr(const struct gobuffer *gb, unsigned int s) 57 | { 58 | return s ? gb->entries + s : NULL; 59 | } 60 | 61 | int gobuffer__allocate(struct gobuffer *gb, unsigned int len) 62 | { 63 | const unsigned int rc = gb->index; 64 | const unsigned int index = gb->index + len; 65 | 66 | if (index >= gb->allocated_size) { 67 | unsigned int allocated_size = (gb->allocated_size + 68 | GOBUFFER__BCHUNK); 69 | if (allocated_size < index) 70 | allocated_size = index + GOBUFFER__BCHUNK; 71 | char *entries = realloc(gb->entries, allocated_size); 72 | 73 | if (entries == NULL) 74 | return -ENOMEM; 75 | 76 | gb->allocated_size = allocated_size; 77 | gb->entries = entries; 78 | } 79 | 80 | gb->index = index; 81 | return rc; 82 | } 83 | 84 | int gobuffer__add(struct gobuffer *gb, const void *s, unsigned int len) 85 | { 86 | const int rc = gobuffer__allocate(gb, len); 87 | 88 | if (rc >= 0) { 89 | ++gb->nr_entries; 90 | memcpy(gb->entries + rc, s, len); 91 | } 92 | return rc; 93 | } 94 | 95 | void gobuffer__copy(const struct gobuffer *gb, void *dest) 96 | { 97 | if (gb->entries) { 98 | memcpy(dest, gb->entries, gobuffer__size(gb)); 99 | } else { 100 | /* gobuffer__size will be 0 or 1. */ 101 | memcpy(dest, "", gobuffer__size(gb)); 102 | } 103 | } 104 | 105 | void gobuffer__sort(struct gobuffer *gb, unsigned int size, int (*compar)(const void *, const void *)) 106 | { 107 | qsort(gb->entries, gb->nr_entries, size, compar); 108 | } 109 | 110 | const void *gobuffer__compress(struct gobuffer *gb, unsigned int *size) 111 | { 112 | z_stream z = { 113 | .zalloc = Z_NULL, 114 | .zfree = Z_NULL, 115 | .opaque = Z_NULL, 116 | .avail_in = gobuffer__size(gb), 117 | .next_in = (Bytef *)(gobuffer__entries(gb) ? : ""), 118 | }; 119 | void *bf = NULL; 120 | unsigned int bf_size = 0; 121 | 122 | if (deflateInit(&z, Z_BEST_COMPRESSION) != Z_OK) 123 | goto out_free; 124 | 125 | do { 126 | const unsigned int new_bf_size = bf_size + GOBUFFER__ZCHUNK; 127 | void *nbf = realloc(bf, new_bf_size); 128 | 129 | if (nbf == NULL) 130 | goto out_close_and_free; 131 | 132 | bf = nbf; 133 | z.avail_out = GOBUFFER__ZCHUNK; 134 | z.next_out = (Bytef *)bf + bf_size; 135 | bf_size = new_bf_size; 136 | if (deflate(&z, Z_FINISH) == Z_STREAM_ERROR) 137 | goto out_close_and_free; 138 | } while (z.avail_out == 0); 139 | 140 | deflateEnd(&z); 141 | *size = bf_size - z.avail_out; 142 | out: 143 | return bf; 144 | 145 | out_close_and_free: 146 | deflateEnd(&z); 147 | out_free: 148 | free(bf); 149 | bf = NULL; 150 | goto out; 151 | } 152 | -------------------------------------------------------------------------------- /gobuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _GOBUFFER_H_ 2 | #define _GOBUFFER_H_ 1 3 | /* 4 | SPDX-License-Identifier: GPL-2.0-only 5 | 6 | Copyright (C) 2008 Arnaldo Carvalho de Melo 7 | */ 8 | 9 | struct gobuffer { 10 | char *entries; 11 | unsigned int nr_entries; 12 | unsigned int index; 13 | unsigned int allocated_size; 14 | }; 15 | 16 | struct gobuffer *gobuffer__new(void); 17 | 18 | void gobuffer__init(struct gobuffer *gb); 19 | void gobuffer__delete(struct gobuffer *gb); 20 | void __gobuffer__delete(struct gobuffer *gb); 21 | 22 | void gobuffer__copy(const struct gobuffer *gb, void *dest); 23 | 24 | void gobuffer__sort(struct gobuffer *gb, unsigned int size, int (*compar)(const void *, const void *)); 25 | 26 | int gobuffer__add(struct gobuffer *gb, const void *s, unsigned int len); 27 | int gobuffer__allocate(struct gobuffer *gb, unsigned int len); 28 | 29 | static inline const void *gobuffer__entries(const struct gobuffer *gb) 30 | { 31 | return gb->entries; 32 | } 33 | 34 | static inline unsigned int gobuffer__nr_entries(const struct gobuffer *gb) 35 | { 36 | return gb->nr_entries; 37 | } 38 | 39 | static inline unsigned int gobuffer__size(const struct gobuffer *gb) 40 | { 41 | return gb->index; 42 | } 43 | 44 | void *gobuffer__ptr(const struct gobuffer *gb, unsigned int s); 45 | 46 | const void *gobuffer__compress(struct gobuffer *gb, unsigned int *size); 47 | 48 | #endif /* _GOBUFFER_H_ */ 49 | -------------------------------------------------------------------------------- /hash.h: -------------------------------------------------------------------------------- 1 | #ifndef _LINUX_HASH_H 2 | #define _LINUX_HASH_H 3 | /* Fast hashing routine for ints, longs and pointers. 4 | (C) 2002 William Lee Irwin III, IBM */ 5 | 6 | /* 7 | * Knuth recommends primes in approximately golden ratio to the maximum 8 | * integer representable by a machine word for multiplicative hashing. 9 | * Chuck Lever verified the effectiveness of this technique: 10 | * http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf 11 | * 12 | * These primes are chosen to be bit-sparse, that is operations on 13 | * them can use shifts and additions instead of multiplications for 14 | * machines where multiplications are slow. 15 | */ 16 | 17 | #include 18 | 19 | static inline uint64_t hash_64(const uint64_t val, const unsigned int bits) 20 | { 21 | return (val * 11400714819323198485LLU) >> (64 - bits); 22 | } 23 | 24 | #endif /* _LINUX_HASH_H */ 25 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := ctracer.o 2 | 3 | ctracer-y := ctracer_collector.o ctracer_relay.o 4 | 5 | # Files generated that shall be removed upon make clean 6 | clean-files := ctracer_collector.c 7 | 8 | CLASS=sock 9 | #KDIR := /home/acme/git/OUTPUT/qemu/linux-2.6/ 10 | KDIR := /lib/modules/$(shell uname -r)/build 11 | PWD := $(shell pwd) 12 | 13 | default: 14 | $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules 15 | 16 | clean: 17 | rm -rf .*.mod.c .*o.cmd *.mod.c *.ko *.o \ 18 | ctracer_collector.c ctracer_methods.stp \ 19 | ctracer_classes.h \ 20 | Module.symvers .tmp_versions/ \ 21 | $(CLASS).{fields,functions} ctracer2ostra* 22 | 23 | $(src)/ctracer2ostra: ctracer_methods.stp 24 | $(CC) $@.c -o $@ 25 | 26 | cu_blacklist_file=/usr/share/dwarves/runtime/linux.blacklist.cu 27 | 28 | LOG=/tmp/ctracer.log 29 | callgraph: ctracer2ostra 30 | ./ctracer2ostra < $(LOG) > $(LOG).ostra ; \ 31 | rm -rf $(CLASS).callgraph ; \ 32 | PYTHONPATH=python/ ostra-cg $(CLASS) $(LOG).ostra 33 | 34 | $(obj)/ctracer_collector.o: ctracer_collector.c 35 | 36 | $(src)/ctracer_collector.c: 37 | ctracer --src_dir $(src) /usr/lib/debug/lib/modules/$(shell uname -r)/vmlinux \ 38 | --cu_blacklist $(cu_blacklist_file) $(CLASS) 39 | -------------------------------------------------------------------------------- /lib/ctracer_relay.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007 Arnaldo Carvalho de Melo 3 | 4 | This program is free software; you can redistribute it and/or modify it 5 | under the terms of version 2 of the GNU General Public License as 6 | published by the Free Software Foundation. 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "ctracer_relay.h" 17 | 18 | static struct rchan *ctracer__rchan; 19 | 20 | static int ctracer__subbuf_start_callback(struct rchan_buf *buf, void *subbuf, 21 | void *prev_subbuf, 22 | size_t prev_padding) 23 | { 24 | static int warned; 25 | if (!relay_buf_full(buf)) 26 | return 1; 27 | if (!warned) { 28 | warned = 1; 29 | printk("relay_buf_full!\n"); 30 | } 31 | return 0; 32 | } 33 | 34 | static struct dentry *ctracer__create_buf_file_callback(const char *filename, 35 | struct dentry *parent, 36 | int mode, 37 | struct rchan_buf *buf, 38 | int *is_global) 39 | { 40 | return debugfs_create_file(filename, mode, parent, buf, 41 | &relay_file_operations); 42 | } 43 | 44 | static int ctracer__remove_buf_file_callback(struct dentry *dentry) 45 | { 46 | debugfs_remove(dentry); 47 | return 0; 48 | } 49 | 50 | static struct rchan_callbacks ctracer__relay_callbacks = { 51 | .subbuf_start = ctracer__subbuf_start_callback, 52 | .create_buf_file = ctracer__create_buf_file_callback, 53 | .remove_buf_file = ctracer__remove_buf_file_callback, 54 | }; 55 | 56 | extern void ctracer__class_state(const void *from, void *to); 57 | 58 | void ctracer__method_hook(const unsigned long long now, 59 | const int probe_type, 60 | const unsigned long long function_id, 61 | const void *object, const int state_len) 62 | { 63 | if (object != NULL) { 64 | void *t = relay_reserve(ctracer__rchan, 65 | sizeof(struct trace_entry) + state_len); 66 | 67 | if (t != NULL) { 68 | struct trace_entry *entry = t; 69 | 70 | entry->nsec = now; 71 | entry->probe_type = probe_type; 72 | entry->object = object; 73 | entry->function_id = function_id; 74 | ctracer__class_state(object, t + sizeof(*entry)); 75 | } 76 | } 77 | } 78 | 79 | EXPORT_SYMBOL_GPL(ctracer__method_hook); 80 | 81 | static int __init ctracer__relay_init(void) 82 | { 83 | ctracer__rchan = relay_open("ctracer", NULL, 512 * 1024, 64, 84 | &ctracer__relay_callbacks, NULL); 85 | if (ctracer__rchan == NULL) { 86 | pr_info("ctracer: couldn't create the relay\n"); 87 | return -1; 88 | } 89 | return 0; 90 | } 91 | 92 | module_init(ctracer__relay_init); 93 | 94 | static void __exit ctracer__relay_exit(void) 95 | { 96 | relay_close(ctracer__rchan); 97 | } 98 | 99 | module_exit(ctracer__relay_exit); 100 | 101 | MODULE_LICENSE("GPL"); 102 | -------------------------------------------------------------------------------- /lib/ctracer_relay.h: -------------------------------------------------------------------------------- 1 | #ifndef _CTRACER_RELAY_H_ 2 | #define _CTRACER_RELAY_H_ 1 3 | /* 4 | Copyright (C) 2007 Arnaldo Carvalho de Melo 5 | 6 | This program is free software; you can redistribute it and/or modify it 7 | under the terms of version 2 of the GNU General Public License as 8 | published by the Free Software Foundation. 9 | */ 10 | 11 | struct trace_entry { 12 | unsigned long long nsec; 13 | unsigned long long probe_type:1; /* Entry or exit */ 14 | unsigned long long function_id:63; 15 | const void *object; 16 | }; 17 | 18 | void ctracer__method_hook(const unsigned long long now, 19 | const int probe_type, 20 | const unsigned long long function, 21 | const void *object, const int state_len); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /lib/include/bpf: -------------------------------------------------------------------------------- 1 | ../bpf/src -------------------------------------------------------------------------------- /lib/linux.blacklist.cu: -------------------------------------------------------------------------------- 1 | kernel/kprobes.c 2 | kernel/relay.c 3 | -------------------------------------------------------------------------------- /libctf.h: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-only 3 | 4 | Copyright (C) 2019 Arnaldo Carvalho de Melo 5 | */ 6 | 7 | #ifndef _LIBCTF_H 8 | #define _LIBCTF_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "gobuffer.h" 16 | #include "elf_symtab.h" 17 | 18 | struct ctf { 19 | void *buf; 20 | void *priv; 21 | Elf *elf; 22 | struct elf_symtab *symtab; 23 | GElf_Ehdr ehdr; 24 | struct gobuffer objects; /* data/variables */ 25 | struct gobuffer types; 26 | struct gobuffer funcs; 27 | struct strings *strings; 28 | char *filename; 29 | size_t size; 30 | int swapped; 31 | int in_fd; 32 | uint8_t wordsize; 33 | uint32_t type_index; 34 | }; 35 | 36 | struct ctf *ctf__new(const char *filename, Elf *elf); 37 | void ctf__delete(struct ctf *ctf); 38 | 39 | bool ctf__ignore_symtab_function(const GElf_Sym *sym, const char *sym_name); 40 | bool ctf__ignore_symtab_object(const GElf_Sym *sym, const char *sym_name); 41 | 42 | int ctf__load(struct ctf *ctf); 43 | 44 | uint16_t ctf__get16(struct ctf *ctf, uint16_t *p); 45 | uint32_t ctf__get32(struct ctf *ctf, uint32_t *p); 46 | void ctf__put16(struct ctf *ctf, uint16_t *p, uint16_t val); 47 | void ctf__put32(struct ctf *ctf, uint32_t *p, uint32_t val); 48 | 49 | void *ctf__get_buffer(struct ctf *ctf); 50 | size_t ctf__get_size(struct ctf *ctf); 51 | 52 | int ctf__load_symtab(struct ctf *ctf); 53 | 54 | uint32_t ctf__add_base_type(struct ctf *ctf, uint32_t name, uint16_t size); 55 | uint32_t ctf__add_fwd_decl(struct ctf *ctf, uint32_t name); 56 | uint32_t ctf__add_short_type(struct ctf *ctf, uint16_t kind, uint16_t type, uint32_t name); 57 | void ctf__add_short_member(struct ctf *ctf, uint32_t name, uint16_t type, 58 | uint16_t offset, int64_t *position); 59 | void ctf__add_full_member(struct ctf *ctf, uint32_t name, uint16_t type, 60 | uint64_t offset, int64_t *position); 61 | uint32_t ctf__add_struct(struct ctf *ctf, uint16_t kind, uint32_t name, 62 | uint64_t size, uint16_t nr_members, int64_t *position); 63 | uint32_t ctf__add_array(struct ctf *ctf, uint16_t type, uint16_t index_type, uint32_t nelems); 64 | void ctf__add_parameter(struct ctf *ctf, uint16_t type, int64_t *position); 65 | uint32_t ctf__add_function_type(struct ctf *ctf, uint16_t type, 66 | uint16_t nr_parms, bool varargs, int64_t *position); 67 | uint32_t ctf__add_enumeration_type(struct ctf *ctf, uint32_t name, uint16_t size, 68 | uint16_t nr_entries, int64_t *position); 69 | void ctf__add_enumerator(struct ctf *ctf, uint32_t name, uint32_t value, 70 | int64_t *position); 71 | 72 | void ctf__add_function_parameter(struct ctf *ctf, uint16_t type, 73 | int64_t *position); 74 | int ctf__add_function(struct ctf *ctf, uint16_t type, uint16_t nr_parms, 75 | bool varargs, int64_t *position); 76 | 77 | int ctf__add_object(struct ctf *ctf, uint16_t type); 78 | 79 | void ctf__set_strings(struct ctf *ctf, struct strings *strings); 80 | int ctf__encode(struct ctf *ctf, uint8_t flags); 81 | 82 | char *ctf__string(struct ctf *ctf, uint32_t ref); 83 | 84 | /** 85 | * ctf__for_each_symtab_function - iterate thru all the symtab functions 86 | * 87 | * @ctf: struct ctf instance to iterate 88 | * @index: uint32_t index 89 | * @sym: GElf_Sym iterator 90 | */ 91 | #define ctf__for_each_symtab_function(ctf, index, sym) \ 92 | elf_symtab__for_each_symbol(ctf->symtab, index, sym) \ 93 | if (ctf__ignore_symtab_function(&sym, \ 94 | elf_sym__name(&sym, \ 95 | ctf->symtab))) \ 96 | continue; \ 97 | else 98 | 99 | /** 100 | * ctf__for_each_symtab_object - iterate thru all the symtab objects 101 | * 102 | * @ctf: struct ctf instance to iterate 103 | * @index: uint32_t index 104 | * @sym: GElf_Sym iterator 105 | */ 106 | #define ctf__for_each_symtab_object(ctf, index, sym) \ 107 | elf_symtab__for_each_symbol(ctf->symtab, index, sym) \ 108 | if (ctf__ignore_symtab_object(&sym, \ 109 | elf_sym__name(&sym, \ 110 | ctf->symtab))) \ 111 | continue; \ 112 | else 113 | 114 | 115 | #endif /* _LIBCTF_H */ 116 | -------------------------------------------------------------------------------- /pdwtags.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-only 3 | 4 | Copyright (C) 2007-2016 Arnaldo Carvalho de Melo 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "dwarves.h" 13 | #include "dutil.h" 14 | 15 | static struct conf_fprintf conf = { 16 | .emit_stats = 1, 17 | }; 18 | 19 | static void emit_tag(struct tag *tag, uint32_t tag_id, struct cu *cu) 20 | { 21 | printf("/* %d */\n", tag_id); 22 | 23 | if (tag__is_struct(tag)) 24 | class__find_holes(tag__class(tag)); 25 | 26 | if (tag->tag == DW_TAG_base_type) { 27 | char bf[64]; 28 | const char *name = base_type__name(tag__base_type(tag), bf, sizeof(bf)); 29 | 30 | if (name == NULL) 31 | printf("anonymous base_type\n"); 32 | else 33 | puts(name); 34 | } else if (tag__is_pointer(tag)) 35 | printf(" /* pointer to %lld */\n", (unsigned long long)tag->type); 36 | else 37 | tag__fprintf(tag, cu, &conf, stdout); 38 | 39 | printf(" /* size: %zd */\n\n", tag__size(tag, cu)); 40 | } 41 | 42 | static int cu__emit_tags(struct cu *cu) 43 | { 44 | uint32_t i; 45 | struct tag *tag; 46 | 47 | puts("/* Types: */\n"); 48 | cu__for_each_type(cu, i, tag) 49 | emit_tag(tag, i, cu); 50 | 51 | puts("/* Functions: */\n"); 52 | conf.no_semicolon = true; 53 | struct function *function; 54 | cu__for_each_function(cu, i, function) { 55 | tag__fprintf(function__tag(function), cu, &conf, stdout); 56 | putchar('\n'); 57 | lexblock__fprintf(&function->lexblock, cu, function, 0, 58 | &conf, stdout); 59 | printf(" /* size: %zd */\n\n", 60 | tag__size(function__tag(function), cu)); 61 | } 62 | conf.no_semicolon = false; 63 | 64 | puts("\n\n/* Variables: */\n"); 65 | cu__for_each_variable(cu, i, tag) { 66 | tag__fprintf(tag, cu, NULL, stdout); 67 | printf(" /* size: %zd */\n\n", tag__size(tag, cu)); 68 | } 69 | 70 | puts("\n\n/* Constants: */\n"); 71 | cu__for_each_constant(cu, i, tag) { 72 | tag__fprintf(tag, cu, NULL, stdout); 73 | printf(" /* size: %zd */\n\n", tag__size(tag, cu)); 74 | } 75 | 76 | bool first_namespace = true; 77 | 78 | cu__for_each_namespace(cu, i, tag) { 79 | if (!tag->top_level) 80 | continue; 81 | 82 | if (first_namespace) { 83 | puts("\n\n/* Namespaces: */\n"); 84 | first_namespace = false; 85 | } 86 | tag__fprintf(tag, cu, NULL, stdout); 87 | puts("\n"); 88 | } 89 | 90 | return 0; 91 | } 92 | 93 | static enum load_steal_kind pdwtags_stealer(struct cu *cu, 94 | struct conf_load *conf_load __maybe_unused) 95 | { 96 | cu__emit_tags(cu); 97 | return LSK__DELETE; 98 | } 99 | 100 | static struct conf_load pdwtags_conf_load = { 101 | .steal = pdwtags_stealer, 102 | .conf_fprintf = &conf, 103 | }; 104 | 105 | /* Name and version of program. */ 106 | ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version; 107 | 108 | static const struct argp_option pdwtags__options[] = { 109 | { 110 | .name = "format_path", 111 | .key = 'F', 112 | .arg = "FORMAT_LIST", 113 | .doc = "List of debugging formats to try" 114 | }, 115 | { 116 | .key = 'V', 117 | .name = "verbose", 118 | .doc = "show details", 119 | }, 120 | { 121 | .name = NULL, 122 | } 123 | }; 124 | 125 | static error_t pdwtags__options_parser(int key, char *arg __maybe_unused, 126 | struct argp_state *state) 127 | { 128 | switch (key) { 129 | case ARGP_KEY_INIT: 130 | if (state->child_inputs != NULL) 131 | state->child_inputs[0] = state->input; 132 | break; 133 | case 'F': pdwtags_conf_load.format_path = arg; break; 134 | case 'V': conf.show_decl_info = 1; break; 135 | default: return ARGP_ERR_UNKNOWN; 136 | } 137 | return 0; 138 | } 139 | 140 | static const char pdwtags__args_doc[] = "FILE"; 141 | 142 | static struct argp pdwtags__argp = { 143 | .options = pdwtags__options, 144 | .parser = pdwtags__options_parser, 145 | .args_doc = pdwtags__args_doc, 146 | }; 147 | 148 | int main(int argc, char *argv[]) 149 | { 150 | int remaining, rc = EXIT_FAILURE, err; 151 | struct cus *cus = cus__new(); 152 | 153 | if (dwarves__init() || cus == NULL) { 154 | fputs("pwdtags: insufficient memory\n", stderr); 155 | goto out; 156 | } 157 | 158 | dwarves__resolve_cacheline_size(&pdwtags_conf_load, 0); 159 | 160 | if (argp_parse(&pdwtags__argp, argc, argv, 0, &remaining, NULL) || 161 | remaining == argc) { 162 | argp_help(&pdwtags__argp, stderr, ARGP_HELP_SEE, argv[0]); 163 | goto out; 164 | } 165 | 166 | err = cus__load_files(cus, &pdwtags_conf_load, argv + remaining); 167 | if (err == 0) { 168 | rc = EXIT_SUCCESS; 169 | goto out; 170 | } 171 | 172 | cus__fprintf_load_files_err(cus, "pdwtags", argv + remaining, err, stderr); 173 | out: 174 | cus__delete(cus); 175 | dwarves__exit(); 176 | return rc; 177 | } 178 | -------------------------------------------------------------------------------- /pglobal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0-only 3 | * 4 | * Copyright (C) 2007 Davi E. M. Arnaut 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "dwarves.h" 16 | #include "dutil.h" 17 | 18 | static int verbose; 19 | 20 | static struct conf_fprintf conf = { 21 | .emit_stats = 1, 22 | }; 23 | 24 | static struct conf_load conf_load = { 25 | .conf_fprintf = &conf, 26 | }; 27 | 28 | struct extvar { 29 | struct extvar *next; 30 | const char *name; 31 | const struct variable *var; 32 | const struct cu *cu; 33 | }; 34 | 35 | struct extfun { 36 | struct extfun *next; 37 | const char *name; 38 | const struct function *fun; 39 | const struct cu *cu; 40 | }; 41 | 42 | static void *tree; 43 | 44 | static void oom(const char *msg) 45 | { 46 | fprintf(stderr, "pglobal: out of memory (%s)\n", msg); 47 | exit(EXIT_FAILURE); 48 | } 49 | 50 | static struct extvar *extvar__new(const struct variable *var, 51 | const struct cu *cu) 52 | { 53 | struct extvar *gvar = malloc(sizeof(*gvar)); 54 | 55 | if (gvar != NULL) { 56 | gvar->next = NULL; 57 | gvar->var = var; 58 | gvar->cu = cu; 59 | gvar->name = variable__name(var); 60 | } 61 | 62 | return gvar; 63 | } 64 | 65 | static struct extfun *extfun__new(struct function *fun, 66 | const struct cu *cu) 67 | { 68 | struct extfun *gfun = malloc(sizeof(*gfun)); 69 | 70 | if (gfun != NULL) { 71 | gfun->next = NULL; 72 | gfun->fun = fun; 73 | gfun->cu = cu; 74 | gfun->name = function__name(fun); 75 | } 76 | 77 | return gfun; 78 | } 79 | 80 | static int extvar__compare(const void *a, const void *b) 81 | { 82 | const struct extvar *ga = a, *gb = b; 83 | return strcmp(ga->name, gb->name); 84 | } 85 | 86 | static int extfun__compare(const void *a, const void *b) 87 | { 88 | const struct extfun *ga = a, *gb = b; 89 | return strcmp(ga->name, gb->name); 90 | } 91 | 92 | static void extvar__add(const struct variable *var, const struct cu *cu) 93 | { 94 | struct extvar **nodep, *gvar = extvar__new(var, cu); 95 | 96 | if (gvar != NULL) { 97 | nodep = tsearch(gvar, &tree, extvar__compare); 98 | if (nodep == NULL) 99 | oom("tsearch"); 100 | else if (*nodep != gvar) { 101 | if (gvar->var->declaration) { 102 | gvar->next = (*nodep)->next; 103 | (*nodep)->next = gvar; 104 | } else { 105 | gvar->next = *nodep; 106 | *nodep = gvar; 107 | } 108 | } 109 | } 110 | } 111 | 112 | static void extfun__add(struct function *fun, const struct cu *cu) 113 | { 114 | struct extfun **nodep, *gfun = extfun__new(fun, cu); 115 | 116 | if (gfun != NULL) { 117 | nodep = tsearch(gfun, &tree, extfun__compare); 118 | if (nodep == NULL) 119 | oom("tsearch"); 120 | else if (*nodep != gfun) { 121 | gfun->next = (*nodep)->next; 122 | (*nodep)->next = gfun; 123 | } 124 | } 125 | } 126 | 127 | static int cu_extvar_iterator(struct cu *cu, void *cookie __maybe_unused) 128 | { 129 | struct tag *pos; 130 | uint32_t id; 131 | 132 | cu__for_each_variable(cu, id, pos) { 133 | struct variable *var = tag__variable(pos); 134 | if (var->external) 135 | extvar__add(var, cu); 136 | } 137 | return 0; 138 | } 139 | 140 | static int cu_extfun_iterator(struct cu *cu, void *cookie __maybe_unused) 141 | { 142 | struct function *pos; 143 | uint32_t id; 144 | 145 | cu__for_each_function(cu, id, pos) 146 | if (pos->external) 147 | extfun__add(pos, cu); 148 | return 0; 149 | } 150 | 151 | static inline const struct extvar *node__variable(const void *nodep) 152 | { 153 | return *((const struct extvar **)nodep); 154 | } 155 | 156 | static inline const struct extfun *node__function(const void *nodep) 157 | { 158 | return *((const struct extfun **)nodep); 159 | } 160 | 161 | static inline struct tag *extvar__tag(const struct extvar *gvar) 162 | { 163 | return (struct tag *)gvar->var; 164 | } 165 | 166 | static inline struct tag *extfun__tag(const struct extfun *gfun) 167 | { 168 | return (struct tag *)gfun->fun; 169 | } 170 | 171 | static void declaration_action__walk(const void *nodep, const VISIT which, 172 | const int depth __maybe_unused) 173 | { 174 | uint32_t count = 0; 175 | struct tag *tag; 176 | const struct extvar *pos, *gvar = NULL; 177 | 178 | switch(which) { 179 | case preorder: 180 | break; 181 | case postorder: 182 | gvar = node__variable(nodep); 183 | break; 184 | case endorder: 185 | break; 186 | case leaf: 187 | gvar = node__variable(nodep); 188 | break; 189 | } 190 | 191 | if (gvar == NULL) 192 | return; 193 | 194 | tag = extvar__tag(gvar); 195 | 196 | tag__fprintf(tag, gvar->cu, NULL, stdout); 197 | 198 | for (pos = gvar->next; pos; pos = pos->next) 199 | count++; 200 | 201 | printf("; /* %u */\n\n", count); 202 | } 203 | 204 | static void function_action__walk(const void *nodep, const VISIT which, 205 | const int depth __maybe_unused) 206 | { 207 | struct tag *tag; 208 | const struct extfun *gfun = NULL; 209 | 210 | switch(which) { 211 | case preorder: 212 | break; 213 | case postorder: 214 | gfun = node__function(nodep); 215 | break; 216 | case endorder: 217 | break; 218 | case leaf: 219 | gfun = node__function(nodep); 220 | break; 221 | } 222 | 223 | if (gfun == NULL) 224 | return; 225 | 226 | tag = extfun__tag(gfun); 227 | 228 | tag__fprintf(tag, gfun->cu, NULL, stdout); 229 | 230 | fputs("\n\n", stdout); 231 | } 232 | 233 | static void free_node(void *nodep) 234 | { 235 | void **node = nodep; 236 | free(*node); 237 | } 238 | 239 | /* Name and version of program. */ 240 | ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version; 241 | 242 | static const struct argp_option pglobal__options[] = { 243 | { 244 | .key = 'v', 245 | .name = "variables", 246 | .doc = "show global variables", 247 | }, 248 | { 249 | .key = 'f', 250 | .name = "functions", 251 | .doc = "show global functions", 252 | }, 253 | { 254 | .name = "format_path", 255 | .key = 'F', 256 | .arg = "FORMAT_LIST", 257 | .doc = "List of debugging formats to try" 258 | }, 259 | { 260 | .key = 'V', 261 | .name = "verbose", 262 | .doc = "be verbose", 263 | }, 264 | { 265 | .name = NULL, 266 | } 267 | }; 268 | 269 | static int walk_var, walk_fun; 270 | 271 | static error_t pglobal__options_parser(int key, char *arg __maybe_unused, 272 | struct argp_state *state) 273 | { 274 | switch (key) { 275 | case ARGP_KEY_INIT: 276 | if (state->child_inputs != NULL) 277 | state->child_inputs[0] = state->input; 278 | break; 279 | case 'v': walk_var = 1; break; 280 | case 'f': walk_fun = 1; break; 281 | case 'V': verbose = 1; break; 282 | case 'F': conf_load.format_path = arg; break; 283 | default: return ARGP_ERR_UNKNOWN; 284 | } 285 | return 0; 286 | } 287 | 288 | static const char pglobal__args_doc[] = "FILE"; 289 | 290 | static struct argp pglobal__argp = { 291 | .options = pglobal__options, 292 | .parser = pglobal__options_parser, 293 | .args_doc = pglobal__args_doc, 294 | }; 295 | 296 | int main(int argc, char *argv[]) 297 | { 298 | int err, remaining, rc = EXIT_FAILURE; 299 | 300 | if (argp_parse(&pglobal__argp, argc, argv, 0, &remaining, NULL) || 301 | remaining == argc) { 302 | argp_help(&pglobal__argp, stderr, ARGP_HELP_SEE, argv[0]); 303 | goto out; 304 | } 305 | 306 | if (dwarves__init()) { 307 | fputs("pglobal: insufficient memory\n", stderr); 308 | goto out; 309 | } 310 | 311 | dwarves__resolve_cacheline_size(&conf_load, 0); 312 | 313 | struct cus *cus = cus__new(); 314 | if (cus == NULL) { 315 | fputs("pglobal: insufficient memory\n", stderr); 316 | goto out_dwarves_exit; 317 | } 318 | 319 | err = cus__load_files(cus, &conf_load, argv + remaining); 320 | if (err != 0) { 321 | cus__fprintf_load_files_err(cus, "pglobal", argv + remaining, err, stderr); 322 | goto out_cus_delete; 323 | } 324 | 325 | if (walk_var) { 326 | cus__for_each_cu(cus, cu_extvar_iterator, NULL, NULL); 327 | twalk(tree, declaration_action__walk); 328 | } else if (walk_fun) { 329 | cus__for_each_cu(cus, cu_extfun_iterator, NULL, NULL); 330 | twalk(tree, function_action__walk); 331 | } 332 | 333 | tdestroy(tree, free_node); 334 | rc = EXIT_SUCCESS; 335 | out_cus_delete: 336 | cus__delete(cus); 337 | out_dwarves_exit: 338 | dwarves__exit(); 339 | out: 340 | return rc; 341 | } 342 | -------------------------------------------------------------------------------- /prefcnt.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-only 3 | 4 | Copyright (C) 2006 Mandriva Conectiva S.A. 5 | Copyright (C) 2006 Arnaldo Carvalho de Melo 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "dwarves.h" 15 | #include "dutil.h" 16 | 17 | static struct conf_fprintf conf; 18 | 19 | static struct conf_load conf_load = { 20 | .conf_fprintf = &conf, 21 | }; 22 | 23 | static void refcnt_tag(struct tag *tag, const struct cu *cu); 24 | 25 | static void refcnt_member(struct class_member *member, const struct cu *cu) 26 | { 27 | if (member->visited) 28 | return; 29 | member->visited = 1; 30 | if (member->tag.type != 0) { /* if not void */ 31 | struct tag *type = cu__type(cu, member->tag.type); 32 | if (type != NULL) 33 | refcnt_tag(type, cu); 34 | } 35 | } 36 | 37 | static void refcnt_parameter(const struct parameter *parameter, 38 | const struct cu *cu) 39 | { 40 | if (parameter->tag.type != 0) { /* if not void */ 41 | struct tag *type = cu__type(cu, parameter->tag.type); 42 | if (type != NULL) 43 | refcnt_tag(type, cu); 44 | } 45 | } 46 | 47 | static void refcnt_variable(const struct variable *variable, 48 | const struct cu *cu) 49 | { 50 | if (variable->ip.tag.type != 0) { /* if not void */ 51 | struct tag *type = cu__type(cu, variable->ip.tag.type); 52 | if (type != NULL) 53 | refcnt_tag(type, cu); 54 | } 55 | } 56 | 57 | static void refcnt_inline_expansion(const struct inline_expansion *exp, 58 | const struct cu *cu) 59 | { 60 | if (exp->ip.tag.type != 0) { /* if not void */ 61 | struct tag *type = cu__function(cu, exp->ip.tag.type); 62 | if (type != NULL) 63 | refcnt_tag(type, cu); 64 | } 65 | } 66 | 67 | static void refcnt_tag(struct tag *tag, const struct cu *cu) 68 | { 69 | struct class_member *member; 70 | 71 | tag->visited = 1; 72 | 73 | if (tag__is_struct(tag) || tag__is_union(tag)) { 74 | type__for_each_member(tag__type(tag), member) 75 | refcnt_member(member, cu); 76 | } 77 | } 78 | 79 | static void refcnt_lexblock(const struct lexblock *lexblock, const struct cu *cu) 80 | { 81 | struct tag *pos; 82 | 83 | list_for_each_entry(pos, &lexblock->tags, node) 84 | switch (pos->tag) { 85 | case DW_TAG_variable: 86 | refcnt_variable(tag__variable(pos), cu); 87 | break; 88 | case DW_TAG_inlined_subroutine: 89 | refcnt_inline_expansion(tag__inline_expansion(pos), cu); 90 | break; 91 | case DW_TAG_lexical_block: 92 | refcnt_lexblock(tag__lexblock(pos), cu); 93 | break; 94 | } 95 | } 96 | 97 | static void refcnt_function(struct function *function, const struct cu *cu) 98 | { 99 | struct parameter *parameter; 100 | 101 | function->proto.tag.visited = 1; 102 | 103 | if (function->proto.tag.type != 0) /* if not void */ { 104 | struct tag *type = cu__type(cu, function->proto.tag.type); 105 | if (type != NULL) 106 | refcnt_tag(type, cu); 107 | } 108 | 109 | list_for_each_entry(parameter, &function->proto.parms, tag.node) 110 | refcnt_parameter(parameter, cu); 111 | 112 | refcnt_lexblock(&function->lexblock, cu); 113 | } 114 | 115 | static int cu_refcnt_iterator(struct cu *cu, void *cookie __maybe_unused) 116 | { 117 | struct function *pos; 118 | uint32_t id; 119 | 120 | cu__for_each_function(cu, id, pos) 121 | refcnt_function(pos, cu); 122 | return 0; 123 | } 124 | 125 | static int lost_iterator(struct tag *tag, struct cu *cu, 126 | void *cookie __maybe_unused) 127 | { 128 | if (!tag->visited && tag__decl_file(tag, cu)) { 129 | tag__fprintf(tag, cu, NULL, stdout); 130 | puts(";\n"); 131 | } 132 | return 0; 133 | } 134 | 135 | static int cu_lost_iterator(struct cu *cu, void *cookie) 136 | { 137 | return cu__for_all_tags(cu, lost_iterator, cookie); 138 | } 139 | 140 | int main(int argc __maybe_unused, char *argv[]) 141 | { 142 | int err; 143 | struct cus *cus = cus__new(); 144 | 145 | if (dwarves__init() || cus == NULL) { 146 | fputs("prefcnt: insufficient memory\n", stderr); 147 | return EXIT_FAILURE; 148 | } 149 | 150 | dwarves__resolve_cacheline_size(NULL, 0); 151 | 152 | err = cus__load_files(cus, &conf_load, argv + 1); 153 | if (err != 0) { 154 | cus__fprintf_load_files_err(cus, "prefcnt", argv + 1, err, stderr); 155 | return EXIT_FAILURE; 156 | } 157 | 158 | cus__for_each_cu(cus, cu_refcnt_iterator, NULL, NULL); 159 | cus__for_each_cu(cus, cu_lost_iterator, NULL, NULL); 160 | 161 | return EXIT_SUCCESS; 162 | } 163 | -------------------------------------------------------------------------------- /rbtree.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | Red Black Trees 5 | (C) 1999 Andrea Arcangeli 6 | (C) 2002 David Woodhouse 7 | 8 | linux/lib/rbtree.c 9 | */ 10 | 11 | #include "rbtree.h" 12 | 13 | static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) 14 | { 15 | struct rb_node *right = node->rb_right; 16 | struct rb_node *parent = rb_parent(node); 17 | 18 | if ((node->rb_right = right->rb_left)) 19 | rb_set_parent(right->rb_left, node); 20 | right->rb_left = node; 21 | 22 | rb_set_parent(right, parent); 23 | 24 | if (parent) 25 | { 26 | if (node == parent->rb_left) 27 | parent->rb_left = right; 28 | else 29 | parent->rb_right = right; 30 | } 31 | else 32 | root->rb_node = right; 33 | rb_set_parent(node, right); 34 | } 35 | 36 | static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) 37 | { 38 | struct rb_node *left = node->rb_left; 39 | struct rb_node *parent = rb_parent(node); 40 | 41 | if ((node->rb_left = left->rb_right)) 42 | rb_set_parent(left->rb_right, node); 43 | left->rb_right = node; 44 | 45 | rb_set_parent(left, parent); 46 | 47 | if (parent) 48 | { 49 | if (node == parent->rb_right) 50 | parent->rb_right = left; 51 | else 52 | parent->rb_left = left; 53 | } 54 | else 55 | root->rb_node = left; 56 | rb_set_parent(node, left); 57 | } 58 | 59 | void rb_insert_color(struct rb_node *node, struct rb_root *root) 60 | { 61 | struct rb_node *parent, *gparent; 62 | 63 | while ((parent = rb_parent(node)) && rb_is_red(parent)) 64 | { 65 | gparent = rb_parent(parent); 66 | 67 | if (parent == gparent->rb_left) 68 | { 69 | { 70 | register struct rb_node *uncle = gparent->rb_right; 71 | if (uncle && rb_is_red(uncle)) 72 | { 73 | rb_set_black(uncle); 74 | rb_set_black(parent); 75 | rb_set_red(gparent); 76 | node = gparent; 77 | continue; 78 | } 79 | } 80 | 81 | if (parent->rb_right == node) 82 | { 83 | register struct rb_node *tmp; 84 | __rb_rotate_left(parent, root); 85 | tmp = parent; 86 | parent = node; 87 | node = tmp; 88 | } 89 | 90 | rb_set_black(parent); 91 | rb_set_red(gparent); 92 | __rb_rotate_right(gparent, root); 93 | } else { 94 | { 95 | register struct rb_node *uncle = gparent->rb_left; 96 | if (uncle && rb_is_red(uncle)) 97 | { 98 | rb_set_black(uncle); 99 | rb_set_black(parent); 100 | rb_set_red(gparent); 101 | node = gparent; 102 | continue; 103 | } 104 | } 105 | 106 | if (parent->rb_left == node) 107 | { 108 | register struct rb_node *tmp; 109 | __rb_rotate_right(parent, root); 110 | tmp = parent; 111 | parent = node; 112 | node = tmp; 113 | } 114 | 115 | rb_set_black(parent); 116 | rb_set_red(gparent); 117 | __rb_rotate_left(gparent, root); 118 | } 119 | } 120 | 121 | rb_set_black(root->rb_node); 122 | } 123 | 124 | static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, 125 | struct rb_root *root) 126 | { 127 | struct rb_node *other; 128 | 129 | while ((!node || rb_is_black(node)) && node != root->rb_node) 130 | { 131 | if (parent->rb_left == node) 132 | { 133 | other = parent->rb_right; 134 | if (rb_is_red(other)) 135 | { 136 | rb_set_black(other); 137 | rb_set_red(parent); 138 | __rb_rotate_left(parent, root); 139 | other = parent->rb_right; 140 | } 141 | if ((!other->rb_left || rb_is_black(other->rb_left)) && 142 | (!other->rb_right || rb_is_black(other->rb_right))) 143 | { 144 | rb_set_red(other); 145 | node = parent; 146 | parent = rb_parent(node); 147 | } 148 | else 149 | { 150 | if (!other->rb_right || rb_is_black(other->rb_right)) 151 | { 152 | rb_set_black(other->rb_left); 153 | rb_set_red(other); 154 | __rb_rotate_right(other, root); 155 | other = parent->rb_right; 156 | } 157 | rb_set_color(other, rb_color(parent)); 158 | rb_set_black(parent); 159 | rb_set_black(other->rb_right); 160 | __rb_rotate_left(parent, root); 161 | node = root->rb_node; 162 | break; 163 | } 164 | } 165 | else 166 | { 167 | other = parent->rb_left; 168 | if (rb_is_red(other)) 169 | { 170 | rb_set_black(other); 171 | rb_set_red(parent); 172 | __rb_rotate_right(parent, root); 173 | other = parent->rb_left; 174 | } 175 | if ((!other->rb_left || rb_is_black(other->rb_left)) && 176 | (!other->rb_right || rb_is_black(other->rb_right))) 177 | { 178 | rb_set_red(other); 179 | node = parent; 180 | parent = rb_parent(node); 181 | } 182 | else 183 | { 184 | if (!other->rb_left || rb_is_black(other->rb_left)) 185 | { 186 | rb_set_black(other->rb_right); 187 | rb_set_red(other); 188 | __rb_rotate_left(other, root); 189 | other = parent->rb_left; 190 | } 191 | rb_set_color(other, rb_color(parent)); 192 | rb_set_black(parent); 193 | rb_set_black(other->rb_left); 194 | __rb_rotate_right(parent, root); 195 | node = root->rb_node; 196 | break; 197 | } 198 | } 199 | } 200 | if (node) 201 | rb_set_black(node); 202 | } 203 | 204 | void rb_erase(struct rb_node *node, struct rb_root *root) 205 | { 206 | struct rb_node *child, *parent; 207 | int color; 208 | 209 | if (!node->rb_left) 210 | child = node->rb_right; 211 | else if (!node->rb_right) 212 | child = node->rb_left; 213 | else 214 | { 215 | struct rb_node *old = node, *left; 216 | 217 | node = node->rb_right; 218 | while ((left = node->rb_left) != NULL) 219 | node = left; 220 | child = node->rb_right; 221 | parent = rb_parent(node); 222 | color = rb_color(node); 223 | 224 | if (child) 225 | rb_set_parent(child, parent); 226 | if (parent == old) { 227 | parent->rb_right = child; 228 | parent = node; 229 | } else 230 | parent->rb_left = child; 231 | 232 | node->rb_parent_color = old->rb_parent_color; 233 | node->rb_right = old->rb_right; 234 | node->rb_left = old->rb_left; 235 | 236 | if (rb_parent(old)) 237 | { 238 | if (rb_parent(old)->rb_left == old) 239 | rb_parent(old)->rb_left = node; 240 | else 241 | rb_parent(old)->rb_right = node; 242 | } else 243 | root->rb_node = node; 244 | 245 | rb_set_parent(old->rb_left, node); 246 | if (old->rb_right) 247 | rb_set_parent(old->rb_right, node); 248 | goto color; 249 | } 250 | 251 | parent = rb_parent(node); 252 | color = rb_color(node); 253 | 254 | if (child) 255 | rb_set_parent(child, parent); 256 | if (parent) 257 | { 258 | if (parent->rb_left == node) 259 | parent->rb_left = child; 260 | else 261 | parent->rb_right = child; 262 | } 263 | else 264 | root->rb_node = child; 265 | 266 | color: 267 | if (color == RB_BLACK) 268 | __rb_erase_color(child, parent, root); 269 | } 270 | 271 | /* 272 | * This function returns the first node (in sort order) of the tree. 273 | */ 274 | struct rb_node *rb_first(const struct rb_root *root) 275 | { 276 | struct rb_node *n; 277 | 278 | n = root->rb_node; 279 | if (!n) 280 | return NULL; 281 | while (n->rb_left) 282 | n = n->rb_left; 283 | return n; 284 | } 285 | 286 | struct rb_node *rb_last(const struct rb_root *root) 287 | { 288 | struct rb_node *n; 289 | 290 | n = root->rb_node; 291 | if (!n) 292 | return NULL; 293 | while (n->rb_right) 294 | n = n->rb_right; 295 | return n; 296 | } 297 | 298 | struct rb_node *rb_next(const struct rb_node *node) 299 | { 300 | struct rb_node *parent; 301 | 302 | if (rb_parent(node) == node) 303 | return NULL; 304 | 305 | /* If we have a right-hand child, go down and then left as far 306 | as we can. */ 307 | if (node->rb_right) { 308 | node = node->rb_right; 309 | while (node->rb_left) 310 | node=node->rb_left; 311 | return (struct rb_node *)node; 312 | } 313 | 314 | /* No right-hand children. Everything down and left is 315 | smaller than us, so any 'next' node must be in the general 316 | direction of our parent. Go up the tree; any time the 317 | ancestor is a right-hand child of its parent, keep going 318 | up. First time it's a left-hand child of its parent, said 319 | parent is our 'next' node. */ 320 | while ((parent = rb_parent(node)) && node == parent->rb_right) 321 | node = parent; 322 | 323 | return parent; 324 | } 325 | 326 | struct rb_node *rb_prev(const struct rb_node *node) 327 | { 328 | struct rb_node *parent; 329 | 330 | if (rb_parent(node) == node) 331 | return NULL; 332 | 333 | /* If we have a left-hand child, go down and then right as far 334 | as we can. */ 335 | if (node->rb_left) { 336 | node = node->rb_left; 337 | while (node->rb_right) 338 | node=node->rb_right; 339 | return (struct rb_node *)node; 340 | } 341 | 342 | /* No left-hand children. Go up till we find an ancestor which 343 | is a right-hand child of its parent */ 344 | while ((parent = rb_parent(node)) && node == parent->rb_left) 345 | node = parent; 346 | 347 | return parent; 348 | } 349 | 350 | void rb_replace_node(struct rb_node *victim, struct rb_node *new, 351 | struct rb_root *root) 352 | { 353 | struct rb_node *parent = rb_parent(victim); 354 | 355 | /* Set the surrounding nodes to point to the replacement */ 356 | if (parent) { 357 | if (victim == parent->rb_left) 358 | parent->rb_left = new; 359 | else 360 | parent->rb_right = new; 361 | } else { 362 | root->rb_node = new; 363 | } 364 | if (victim->rb_left) 365 | rb_set_parent(victim->rb_left, new); 366 | if (victim->rb_right) 367 | rb_set_parent(victim->rb_right, new); 368 | 369 | /* Copy the pointers/colour from the victim to the replacement */ 370 | *new = *victim; 371 | } 372 | -------------------------------------------------------------------------------- /rbtree.h: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | Red Black Trees 5 | (C) 1999 Andrea Arcangeli 6 | 7 | linux/include/linux/rbtree.h 8 | 9 | To use rbtrees you'll have to implement your own insert and search cores. 10 | This will avoid us to use callbacks and to drop drammatically performances. 11 | I know it's not the cleaner way, but in C (not in C++) to get 12 | performances and genericity... 13 | 14 | Some example of insert and search follows here. The search is a plain 15 | normal search over an ordered tree. The insert instead must be implemented 16 | int two steps: as first thing the code must insert the element in 17 | order as a red leaf in the tree, then the support library function 18 | rb_insert_color() must be called. Such function will do the 19 | not trivial work to rebalance the rbtree if necessary. 20 | 21 | ----------------------------------------------------------------------- 22 | static inline struct page * rb_search_page_cache(struct inode * inode, 23 | unsigned long offset) 24 | { 25 | struct rb_node * n = inode->i_rb_page_cache.rb_node; 26 | struct page * page; 27 | 28 | while (n) 29 | { 30 | page = rb_entry(n, struct page, rb_page_cache); 31 | 32 | if (offset < page->offset) 33 | n = n->rb_left; 34 | else if (offset > page->offset) 35 | n = n->rb_right; 36 | else 37 | return page; 38 | } 39 | return NULL; 40 | } 41 | 42 | static inline struct page * __rb_insert_page_cache(struct inode * inode, 43 | unsigned long offset, 44 | struct rb_node * node) 45 | { 46 | struct rb_node ** p = &inode->i_rb_page_cache.rb_node; 47 | struct rb_node * parent = NULL; 48 | struct page * page; 49 | 50 | while (*p) 51 | { 52 | parent = *p; 53 | page = rb_entry(parent, struct page, rb_page_cache); 54 | 55 | if (offset < page->offset) 56 | p = &(*p)->rb_left; 57 | else if (offset > page->offset) 58 | p = &(*p)->rb_right; 59 | else 60 | return page; 61 | } 62 | 63 | rb_link_node(node, parent, p); 64 | 65 | return NULL; 66 | } 67 | 68 | static inline struct page * rb_insert_page_cache(struct inode * inode, 69 | unsigned long offset, 70 | struct rb_node * node) 71 | { 72 | struct page * ret; 73 | if ((ret = __rb_insert_page_cache(inode, offset, node))) 74 | goto out; 75 | rb_insert_color(node, &inode->i_rb_page_cache); 76 | out: 77 | return ret; 78 | } 79 | ----------------------------------------------------------------------- 80 | */ 81 | 82 | #ifndef _LINUX_RBTREE_H 83 | #define _LINUX_RBTREE_H 84 | 85 | #include 86 | 87 | /** 88 | * container_of - cast a member of a structure out to the containing structure 89 | * @ptr: the pointer to the member. 90 | * @type: the type of the container struct this is embedded in. 91 | * @member: the name of the member within the struct. 92 | * 93 | */ 94 | #define container_of(ptr, type, member) ({ \ 95 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 96 | (type *)( (char *)__mptr - offsetof(type,member) );}) 97 | 98 | struct rb_node 99 | { 100 | unsigned long rb_parent_color; 101 | #define RB_RED 0 102 | #define RB_BLACK 1 103 | struct rb_node *rb_right; 104 | struct rb_node *rb_left; 105 | } __attribute__((aligned(sizeof(long)))); 106 | /* The alignment might seem pointless, but allegedly CRIS needs it */ 107 | 108 | struct rb_root 109 | { 110 | struct rb_node *rb_node; 111 | }; 112 | 113 | 114 | #define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) 115 | #define rb_color(r) ((r)->rb_parent_color & 1) 116 | #define rb_is_red(r) (!rb_color(r)) 117 | #define rb_is_black(r) rb_color(r) 118 | #define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) 119 | #define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) 120 | 121 | static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) 122 | { 123 | rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; 124 | } 125 | static inline void rb_set_color(struct rb_node *rb, int color) 126 | { 127 | rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; 128 | } 129 | 130 | #define RB_ROOT (struct rb_root) { NULL, } 131 | #define rb_entry(ptr, type, member) container_of(ptr, type, member) 132 | 133 | #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) 134 | #define RB_EMPTY_NODE(node) (rb_parent(node) == node) 135 | #define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) 136 | 137 | extern void rb_insert_color(struct rb_node *, struct rb_root *); 138 | extern void rb_erase(struct rb_node *, struct rb_root *); 139 | 140 | /* Find logical next and previous nodes in a tree */ 141 | extern struct rb_node *rb_next(const struct rb_node *); 142 | extern struct rb_node *rb_prev(const struct rb_node *); 143 | extern struct rb_node *rb_first(const struct rb_root *); 144 | extern struct rb_node *rb_last(const struct rb_root *); 145 | 146 | /* Fast replacement of a single node without remove/rebalance/add/rebalance */ 147 | extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, 148 | struct rb_root *root); 149 | 150 | static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, 151 | struct rb_node ** rb_link) 152 | { 153 | node->rb_parent_color = (unsigned long )parent; 154 | node->rb_left = node->rb_right = NULL; 155 | 156 | *rb_link = node; 157 | } 158 | 159 | #endif /* _LINUX_RBTREE_H */ 160 | -------------------------------------------------------------------------------- /regtest: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # -*- python -*- 3 | # -*- coding: utf-8 -*- 4 | # tuna - Application Tuning GUI 5 | # Copyright (C) 2009 Arnaldo Carvalho de Melo 6 | # Arnaldo Carvalho de Melo 7 | # 8 | # This application is free software; you can redistribute it and/or 9 | # modify it under the terms of the GNU General Public License 10 | # as published by the Free Software Foundation; version 2. 11 | # 12 | # This application is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | 17 | import filecmp, getopt, os, posix, signal, sys, tempfile 18 | 19 | regtest_output_dir = "/media/tb/pahole/regtest/" 20 | regtest_obj_dir = "/media/tb/debuginfo/usr/lib/debug/" 21 | tools = {"pahole": { "dwarf": "--flat_arrays --show_private_classes --fixup_silly_bitfields --first_obj_only --classes_as_structs" }} 22 | all_formats = ("ctf", "dwarf") 23 | formats = all_formats 24 | len_debug_dir = len(regtest_obj_dir) 25 | verbose = 1 26 | 27 | # Turn this on when testing CTF generated files 28 | use_options = False 29 | 30 | def diff_file(from_filename, to_filename): 31 | fd, diff_filename = tempfile.mkstemp() 32 | command = 'diff -up "%s" "%s" > %s' % (from_filename, 33 | to_filename, diff_filename) 34 | if verbose > 1: 35 | print command 36 | try: 37 | os.system(command) 38 | os.system("vim %s" % diff_filename) 39 | finally: 40 | os.unlink(diff_filename) 41 | 42 | def dir_has_no_diffs(dirname): 43 | return os.access(os.path.join(dirname, ".no_diffs"), os.F_OK) 44 | 45 | def set_dir_has_no_diffs(dirname): 46 | f = file(os.path.join(dirname, ".no_diffs"), "w") 47 | f.close() 48 | 49 | def reset_dir_has_no_diffs(dirname): 50 | os.unlink(os.path.join(dirname, ".no_diffs")) 51 | 52 | def diff_dir(from_dir, to_dir, dir = None, recursive = True): 53 | if dir: 54 | from_dir = os.path.join(from_dir, dir) 55 | to_dir = os.path.join(to_dir, dir) 56 | print "\r%-130s" % from_dir 57 | sys.stdout.flush() 58 | diff = filecmp.dircmp(from_dir, to_dir) 59 | if not dir_has_no_diffs(to_dir): 60 | diff_files = diff.diff_files 61 | if diff_files: 62 | diff_files.sort() 63 | print "\n %s" % from_dir 64 | sys.stdout.flush() 65 | for f in diff_files: 66 | diff_file(os.path.join(from_dir, f), 67 | os.path.join(to_dir, f)) 68 | else: 69 | set_dir_has_no_diffs(to_dir) 70 | if not recursive: 71 | return 72 | common_dirs = diff.common_dirs 73 | if not common_dirs: 74 | return 75 | common_dirs.sort() 76 | for dir in common_dirs: 77 | diff_dir(from_dir, to_dir, dir) 78 | 79 | def do_diff_dwarfs2ctfs(): 80 | diff_dir(os.path.join(regtest_output_dir, "after", "pahole", "dwarf"), 81 | os.path.join(regtest_output_dir, "after", "pahole", "ctf")) 82 | 83 | def do_diff_dwarfs(): 84 | diff_dir(os.path.join(regtest_output_dir, "before", "pahole", "dwarf"), 85 | os.path.join(regtest_output_dir, "after", "pahole", "dwarf")) 86 | 87 | def do_tool(tool, before_after, format, dirname, fname, 88 | prepend_obj_dir = False): 89 | if prepend_obj_dir: 90 | fname += ".debug" 91 | fixed_dirname = dirname 92 | else: 93 | fixed_dirname = dirname[len_debug_dir:] 94 | tool_output_dir = os.path.join(regtest_output_dir, 95 | before_after, tool, format, 96 | fixed_dirname) 97 | obj_path = os.path.join(dirname, fname) 98 | if prepend_obj_dir: 99 | obj_path = os.path.join(regtest_obj_dir, obj_path) 100 | if os.path.islink(obj_path) or os.path.isdir(obj_path): 101 | return 102 | try: 103 | os.makedirs(tool_output_dir) 104 | except: 105 | pass 106 | if dir_has_no_diffs(tool_output_dir): 107 | reset_dir_has_no_diffs(tool_output_dir) 108 | output_file = os.path.join(tool_output_dir, fname[:-6]) 109 | if use_options and tools[tool].has_key(format): 110 | options = tools[tool][format] 111 | else: 112 | options = "" 113 | command = '%s -F %s %s %s > "%s"' % (tool, format, options, 114 | obj_path, output_file) 115 | if verbose > 1: 116 | print command 117 | sys.stdout.flush() 118 | elif verbose > 0: 119 | print "%s: %s" % (format, 120 | os.path.join(fixed_dirname, fname[:-6])) 121 | os.system(command) 122 | 123 | def do_tool_on_files(arg, dirname, fnames, prepend_obj_dir = False): 124 | if dirname.find("/.") >= 0: 125 | return 126 | tool, before_after = arg 127 | for fname in fnames: 128 | if not prepend_obj_dir and fname[-6:] != ".debug": 129 | continue 130 | 131 | for format in formats: 132 | do_tool(tool, before_after, format, dirname, fname, 133 | prepend_obj_dir) 134 | 135 | def do_tools(before_after): 136 | for tool in tools.keys(): 137 | os.path.walk(regtest_obj_dir, do_tool_on_files, (tool, before_after)) 138 | 139 | def do_ctf(dirname, fname, prepend_obj_dir = False): 140 | if prepend_obj_dir: 141 | fname += ".debug" 142 | fixed_dirname = dirname 143 | else: 144 | fixed_dirname = dirname[len_debug_dir:] 145 | obj_path = os.path.join(dirname, fname) 146 | if prepend_obj_dir: 147 | obj_path = os.path.join(regtest_obj_dir, obj_path) 148 | 149 | if os.path.islink(obj_path) or os.path.isdir(obj_path): 150 | return 151 | command = 'pahole -Z "%s" 2> /dev/null' % obj_path 152 | if verbose > 1: 153 | print command 154 | elif verbose > 0: 155 | print os.path.join(fixed_dirname, fname[:-6]) 156 | os.system(command) 157 | 158 | def do_ctf_on_files(arg, dirname, fnames, prepend_obj_dir = False): 159 | if dirname.find("/.") >= 0: 160 | return 161 | for fname in fnames: 162 | if not prepend_obj_dir and fname[-6:] != ".debug": 163 | continue 164 | 165 | do_ctf(dirname, fname, prepend_obj_dir) 166 | 167 | def do_ctfs(): 168 | os.path.walk(regtest_obj_dir, do_ctf_on_files, None) 169 | 170 | def sig_exit(sig_number, stack_frame): 171 | sys.exit(1) 172 | 173 | def listdebugs(dirname): 174 | fnames = [] 175 | for fname in os.listdir(os.path.join(regtest_obj_dir, dirname)): 176 | if fname[-6:] != ".debug": 177 | continue 178 | obj_path = os.path.join(regtest_obj_dir, dirname, fname) 179 | if os.path.islink(obj_path) or os.path.isdir(obj_path): 180 | continue 181 | fnames.append(fname[:-6]) 182 | return fnames 183 | 184 | def usage(): 185 | print 'Usage: regtest [OPTIONS]' 186 | fmt = '\t%-20s %s' 187 | print fmt % ('-h, --help', 'Give this help list') 188 | print fmt % ('-a, --after', 'Generate new output') 189 | print fmt % ('-b, --before', 'Generate old output') 190 | print fmt % ('-c, --ctf_diff', 'Diff between DWARF and CTF for new output') 191 | print fmt % ('-C, --ctf_encode', 'Encode CTF into object files') 192 | print fmt % ('-d, --diff', 'Diff between old and new output') 193 | print fmt % ('-f, --formats', 'formats used (default: %s)' ','.join(formats)) 194 | 195 | def main(argv): 196 | global formats 197 | 198 | for sig in (signal.SIGHUP, signal.SIGINT, signal.SIGTERM): 199 | signal.signal(sig, sig_exit) 200 | 201 | try: 202 | short = "habcCdf:" 203 | long = ("help", "after", "before", "ctf_diff", "ctf_encode", 204 | "diff", "formats") 205 | opts, args = getopt.getopt(sys.argv[1:], short, long) 206 | except getopt.GetoptError, err: 207 | usage() 208 | print str(err) 209 | sys.exit(2) 210 | 211 | for o, a in opts: 212 | if o in ("-h", "--help"): 213 | usage() 214 | return 215 | elif o in ("-f", "--formats"): 216 | formats = a.split(',') 217 | elif o in ("-a", "--after", 218 | "-b", "--before", 219 | "-c", "--ctf_diff", 220 | "-C", "--ctf_encode", 221 | "-d", "--diff"): 222 | 223 | if len(args) > 0: 224 | dirname = args[0] 225 | if len(args) > 1: 226 | fnames = args[1:] 227 | elif o in ('-a', '--after', 228 | '-b', '--before', 229 | '-C', '--ctf_encode'): 230 | fnames = listdebugs(dirname) 231 | 232 | if o in ('-b', '--before', '-a', '--after'): 233 | if o in ('-b', '--before'): 234 | when = 'before' 235 | else: 236 | when = 'after' 237 | if len(args) > 0: 238 | for tool in tools.keys(): 239 | arg = (tool, when) 240 | do_tool_on_files(arg, dirname, fnames, True) 241 | else: 242 | do_tools(when) 243 | elif o in ('-d', '--diff'): 244 | if len(args) > 0: 245 | from_dir = os.path.join(regtest_output_dir, 246 | "before", "pahole", 247 | "dwarf", dirname) 248 | to_dir = os.path.join(regtest_output_dir, 249 | "after", "pahole", 250 | "dwarf", dirname) 251 | if len(args) > 1: 252 | for fname in fnames: 253 | diff_file(os.path.join(from_dir, fname), 254 | os.path.join(to_dir, fname)) 255 | else: 256 | diff_dir(from_dir, to_dir, recursive = False) 257 | else: 258 | do_diff_dwarfs() 259 | elif o in ('-C', 'ctf'): 260 | if len(args) > 0: 261 | do_ctf_on_files(None, dirname, fnames, True) 262 | else: 263 | do_ctfs() 264 | elif o in ('-c', 'ctf_diff'): 265 | if len(args) > 0: 266 | from_dir = os.path.join(regtest_output_dir, 267 | "after", "pahole", 268 | "dwarf", dirname) 269 | to_dir = os.path.join(regtest_output_dir, 270 | "after", "pahole", 271 | "ctf", dirname) 272 | if len(args) > 1: 273 | for fname in fnames: 274 | diff_file(os.path.join(from_dir, fname), 275 | os.path.join(to_dir, fname)) 276 | else: 277 | diff_dir(from_dir, to_dir, recursive = False) 278 | else: 279 | do_diff_dwarfs2ctfs() 280 | 281 | if __name__ == '__main__': 282 | main(sys.argv) 283 | -------------------------------------------------------------------------------- /scncopy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: GPL-2.0-only 3 | * 4 | * Copyright 2009 Red Hat, Inc. 5 | * 6 | * Author: Peter Jones 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "elfcreator.h" 17 | #include "dutil.h" 18 | 19 | static int should_copy_scn(Elf *elf, GElf_Shdr *shdr, struct strlist *scns) 20 | { 21 | char *name; 22 | size_t shstrndx; 23 | 24 | if (elf_getshdrstrndx(elf, &shstrndx) < 0) 25 | return 0; 26 | name = elf_strptr(elf, shstrndx, shdr->sh_name); 27 | if (name == NULL) 28 | return 0; 29 | 30 | if (strlist__has_entry(scns, name)) 31 | return 1; 32 | return 0; 33 | } 34 | 35 | int main(int argc, char *argv[]) 36 | { 37 | int n; 38 | struct strlist *sections; 39 | char *infile = NULL, *outfile = NULL; 40 | int fd; 41 | Elf *elf; 42 | Elf_Scn *scn; 43 | int copy_all_sections = 0; 44 | ElfCreator *ctor; 45 | 46 | sections = strlist__new(false); 47 | for (n = 1; n < argc; n++) { 48 | if (!strcmp(argv[n], "-a")) { 49 | copy_all_sections = 1; 50 | } else if (!strcmp(argv[n], "-s")) { 51 | if (n == argc-1) { 52 | fprintf(stderr, "Missing argument to -s\n"); 53 | return -1; 54 | } 55 | n++; 56 | strlist__add(sections, argv[n]); 57 | continue; 58 | } else if (!strcmp(argv[n], "-o")) { 59 | if (n == argc-1) { 60 | fprintf(stderr, "Missing argument to -o\n"); 61 | return -1; 62 | } 63 | n++; 64 | outfile = argv[n]; 65 | continue; 66 | } else if (!strcmp(argv[n], "-?") || 67 | !strcmp(argv[n], "--help") || 68 | !strcmp(argv[n], "--usage")) { 69 | printf("usage: scncopy [-s section0 [[-s section1] ... -s sectionN] | -a ] -o outfile infile\n"); 70 | return 0; 71 | } else if (n == argc-1) { 72 | infile = argv[n]; 73 | } else { 74 | fprintf(stderr, "usage: pjoc -s section 0 [[-s section1] ... -s sectionN] -o outfile infile\n"); 75 | return 1; 76 | } 77 | } 78 | if (!infile || !outfile) { 79 | fprintf(stderr, "usage: pjoc -s section 0 [[-s section1] ... -s sectionN] -o outfile infile\n"); 80 | return 1; 81 | } 82 | 83 | if (!(fd = open(infile, O_RDONLY))) { 84 | fprintf(stderr, "Could not open \"%s\" for reading: %m\n", infile); 85 | return 1; 86 | } 87 | 88 | elf_version(EV_CURRENT); 89 | 90 | if ((elf = elf_begin(fd, ELF_C_READ_MMAP_PRIVATE, NULL)) == NULL) { 91 | fprintf(stderr, "cannot get elf descriptor for \"%s\": %s\n", 92 | infile, elf_errmsg(-1)); 93 | close(fd); 94 | return 1; 95 | } 96 | 97 | if (elf_kind(elf) != ELF_K_ELF) { 98 | fprintf(stderr, "\"%s\" is not an ELF file\n", infile); 99 | err: 100 | elf_end(elf); 101 | close(fd); 102 | return 1; 103 | } 104 | 105 | if ((ctor = elfcreator_begin(outfile, elf)) == NULL) { 106 | fprintf(stderr, "could not initialize ELF creator\n"); 107 | goto err; 108 | } 109 | 110 | scn = NULL; 111 | while ((scn = elf_nextscn(elf, scn)) != NULL) { 112 | GElf_Shdr shdr_mem, *shdr; 113 | 114 | shdr = gelf_getshdr(scn, &shdr_mem); 115 | if (shdr == NULL) 116 | continue; 117 | 118 | if (!should_copy_scn(elf, shdr, sections) && !copy_all_sections) 119 | continue; 120 | 121 | elfcreator_copy_scn(ctor, scn); 122 | } 123 | elfcreator_end(ctor); 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /syscse.c: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: GPL-2.0-only 3 | 4 | Copyright (C) 2007-2016 Arnaldo Carvalho de Melo 5 | 6 | System call sign extender 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "dwarves.h" 16 | #include "dutil.h" 17 | 18 | static const char *prefix = "sys_"; 19 | static size_t prefix_len = 4; 20 | 21 | static struct conf_fprintf conf; 22 | 23 | static struct conf_load conf_load = { 24 | .conf_fprintf = &conf, 25 | }; 26 | 27 | static bool filter(struct function *f) 28 | { 29 | if (f->proto.nr_parms != 0) { 30 | const char *name = function__name(f); 31 | 32 | if (strlen(name) > prefix_len && 33 | memcmp(name, prefix, prefix_len) == 0) 34 | return false; 35 | } 36 | return true; 37 | } 38 | 39 | static void zero_extend(const int regparm, const struct base_type *bt, const char *parm) 40 | { 41 | const char *instr = "INVALID"; 42 | 43 | switch (bt->bit_size) { 44 | case 32: 45 | instr = "sll"; 46 | break; 47 | case 16: 48 | instr = "slw"; 49 | break; 50 | case 8: 51 | instr = "slb"; 52 | break; 53 | } 54 | 55 | char bf[64]; 56 | printf("\t%s\t$a%d, $a%d, 0" 57 | "\t/* zero extend $a%d(%s %s) from %d to 64-bit */\n", 58 | instr, regparm, regparm, regparm, 59 | base_type__name(bt, bf, sizeof(bf)), 60 | parm, bt->bit_size); 61 | } 62 | 63 | static void emit_wrapper(struct function *f, struct cu *cu) 64 | { 65 | struct parameter *parm; 66 | const char *name = function__name(f); 67 | int regparm = 0, needs_wrapper = 0; 68 | 69 | function__for_each_parameter(f, cu, parm) { 70 | const type_id_t type_id = parm->tag.type; 71 | struct tag *type = cu__type(cu, type_id); 72 | 73 | tag__assert_search_result(type, parm->tag.tag, parameter__name(parm)); 74 | if (type->tag == DW_TAG_base_type) { 75 | struct base_type *bt = tag__base_type(type); 76 | char bf[64]; 77 | 78 | if (bt->bit_size < 64 && 79 | strncmp(base_type__name(bt, bf, sizeof(bf)), "unsigned", 8) == 0) { 80 | if (!needs_wrapper) { 81 | printf("wrap_%s:\n", name); 82 | needs_wrapper = 1; 83 | } 84 | zero_extend(regparm, bt, parameter__name(parm)); 85 | } 86 | } 87 | ++regparm; 88 | } 89 | 90 | if (needs_wrapper) 91 | printf("\tj\t%s\n\n", name); 92 | } 93 | 94 | static int cu__emit_wrapper(struct cu *cu, void *cookie __maybe_unused) 95 | { 96 | struct function *pos; 97 | uint32_t id; 98 | 99 | cu__for_each_function(cu, id, pos) 100 | if (!filter(pos)) 101 | emit_wrapper(pos, cu); 102 | return 0; 103 | } 104 | 105 | static void cus__emit_wrapper(struct cus *cu) 106 | { 107 | cus__for_each_cu(cu, cu__emit_wrapper, NULL, NULL); 108 | } 109 | 110 | /* Name and version of program. */ 111 | ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version; 112 | 113 | static const struct argp_option options[] = { 114 | { 115 | .key = 'p', 116 | .name = "prefix", 117 | .arg = "PREFIX", 118 | .doc = "function prefix", 119 | }, 120 | { 121 | .name = NULL, 122 | } 123 | }; 124 | 125 | static error_t options_parser(int key, char *arg, struct argp_state *state) 126 | { 127 | switch (key) { 128 | case ARGP_KEY_INIT: 129 | if (state->child_inputs != NULL) 130 | state->child_inputs[0] = state->input; 131 | break; 132 | case 'p': 133 | prefix = arg; 134 | prefix_len = strlen(prefix); 135 | break; 136 | default: 137 | return ARGP_ERR_UNKNOWN; 138 | } 139 | return 0; 140 | } 141 | 142 | static const char args_doc[] = "FILE"; 143 | 144 | static struct argp argp = { 145 | .options = options, 146 | .parser = options_parser, 147 | .args_doc = args_doc, 148 | }; 149 | 150 | int main(int argc, char *argv[]) 151 | { 152 | int err, remaining; 153 | struct cus *cus = cus__new(); 154 | 155 | if (cus == NULL) { 156 | fprintf(stderr, "%s: insufficient memory\n", argv[0]); 157 | return EXIT_FAILURE; 158 | } 159 | 160 | if (argp_parse(&argp, argc, argv, 0, &remaining, NULL) || 161 | remaining == argc) { 162 | argp_help(&argp, stderr, ARGP_HELP_SEE, argv[0]); 163 | return EXIT_FAILURE; 164 | } 165 | err = cus__load_files(cus, &conf_load, argv + remaining); 166 | if (err != 0) { 167 | cus__fprintf_load_files_err(cus, "syscse", argv + remaining, err, stderr); 168 | return EXIT_FAILURE; 169 | } 170 | 171 | cus__emit_wrapper(cus); 172 | return EXIT_SUCCESS; 173 | } 174 | -------------------------------------------------------------------------------- /tests/btf_functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # Copyright (c) 2024, Oracle and/or its affiliates. 5 | # 6 | # Examine functions - especially those for which we skipped BTF encoding - 7 | # to validate that they were indeed skipped for BTF encoding, and that they 8 | # also should have been. 9 | # 10 | 11 | outdir= 12 | 13 | fail() 14 | { 15 | # Do not remove test dir; might be useful for analysis 16 | trap - EXIT 17 | if [[ -d "$outdir" ]]; then 18 | echo "Test data is in $outdir" 19 | fi 20 | exit 1 21 | } 22 | 23 | cleanup() 24 | { 25 | rm ${outdir}/* 26 | rmdir $outdir 27 | } 28 | 29 | vmlinux=${vmlinux:-$1} 30 | 31 | if [ -z "$vmlinux" ] ; then 32 | vmlinux=$(pahole --running_kernel_vmlinux) 33 | if [ -z "$vmlinux" ] ; then 34 | echo "Please specify a vmlinux file to operate on" 35 | exit 2 36 | fi 37 | fi 38 | 39 | if [ ! -f "$vmlinux" ] ; then 40 | echo "$vmlinux file not available, please specify another" 41 | exit 2 42 | fi 43 | 44 | outdir=$(mktemp -d /tmp/btf_functions.sh.XXXXXX) 45 | 46 | trap cleanup EXIT 47 | 48 | echo -n "Validation of BTF encoding of functions; this may take some time: " 49 | test -n "$VERBOSE" && printf "\nEncoding..." 50 | 51 | # Here we use both methods so that we test pahole --lang_exclude, that is 52 | # used in the Linux kernel BTF encoding phase, and as well to make sure all 53 | # other pahole and pfunct use in this script will exclude the Rust CUs, testing 54 | # the fallback to PAHOLE_LANG_EXCLUDE. 55 | export PAHOLE_LANG_EXCLUDE=rust 56 | 57 | pahole --btf_features=default --lang_exclude=rust --btf_encode_detached=$outdir/vmlinux.btf --verbose $vmlinux |\ 58 | grep "skipping BTF encoding of function" > ${outdir}/skipped_fns 59 | 60 | test -n "$VERBOSE" && printf "done.\n" 61 | 62 | funcs=$(pfunct --format_path=btf $outdir/vmlinux.btf 2>/dev/null|sort) 63 | 64 | # all functions from DWARF; some inline functions are not inlined so include them too 65 | pfunct --all --no_parm_names --format_path=dwarf $vmlinux | \ 66 | sort|uniq > $outdir/dwarf.funcs 67 | # all functions from BTF (removing bpf_kfunc prefix where found) 68 | pfunct --all --no_parm_names --format_path=btf $outdir/vmlinux.btf 2>/dev/null|\ 69 | awk '{ gsub("^(bpf_kfunc |bpf_fastcall )+",""); print $0}'|sort|uniq > $outdir/btf.funcs 70 | 71 | exact=0 72 | inline=0 73 | const_insensitive=0 74 | 75 | while IFS= read -r btf ; do 76 | # look for non-inline match first 77 | dwarf=$(grep -F "$btf" $outdir/dwarf.funcs) 78 | if [[ "$btf" != "$dwarf" ]]; then 79 | # function might be declared inline in DWARF. 80 | if [[ "inline $btf" != "$dwarf" ]]; then 81 | # some functions have multiple instances in DWARF where one has 82 | # const param(s) and another does not (see errpos()). We do not 83 | # mark these functions inconsistent as though they technically 84 | # have different prototypes, the data itself is not different. 85 | btf_noconst=$(echo $btf | awk '{gsub("const ",""); print $0 }') 86 | dwarf_noconst=$(echo $dwarf | awk '{gsub("const ",""); print $0 }') 87 | if [[ "$dwarf_noconst" =~ "$btf_noconst" ]]; then 88 | const_insensitive=$((const_insensitive+1)) 89 | else 90 | echo "ERROR: mismatch : BTF '$btf' not found; DWARF '$dwarf'" 91 | fail 92 | fi 93 | else 94 | inline=$((inline+1)) 95 | fi 96 | else 97 | exact=$((exact+1)) 98 | fi 99 | done < $outdir/btf.funcs 100 | 101 | if [[ -n "$VERBOSE" ]]; then 102 | echo "Matched $exact functions exactly." 103 | echo "Matched $inline functions with inlines." 104 | echo "Matched $const_insensitive functions with multiple const/non-const instances." 105 | echo "Ok" 106 | echo "Validation of skipped function logic..." 107 | fi 108 | 109 | skipped_cnt=$(wc -l ${outdir}/skipped_fns | awk '{ print $1}') 110 | 111 | if [[ "$skipped_cnt" == "0" ]]; then 112 | echo "No skipped functions. Done." 113 | exit 0 114 | fi 115 | 116 | skipped_fns=$(awk '{print $1}' $outdir/skipped_fns) 117 | for s in $skipped_fns ; do 118 | # Ensure the skipped function are not in BTF 119 | inbtf=$(grep " $s(" $outdir/btf.funcs) 120 | if [[ -n "$inbtf" ]]; then 121 | echo "ERROR: '${s}()' was added incorrectly to BTF: '$inbtf'" 122 | fail 123 | fi 124 | done 125 | 126 | if [[ -n "$VERBOSE" ]]; then 127 | echo "Skipped encoding $skipped_cnt functions in BTF." 128 | echo "Ok" 129 | echo "Validating skipped functions have incompatible return values..." 130 | fi 131 | 132 | return_mismatches=$(awk '/return type mismatch/ { print $1 }' $outdir/skipped_fns) 133 | return_count=0 134 | 135 | for r in $return_mismatches ; do 136 | # Ensure there are multiple instances with incompatible return values 137 | grep " $r(" $outdir/dwarf.funcs | \ 138 | awk -v FN=$r '{i = index($0, FN); if (i>0) print substr($0, 0, i-1) }' \ 139 | | uniq > ${outdir}/retvals.$r 140 | cnt=$(wc -l ${outdir}/retvals.$r | awk '{ print $1 }') 141 | if [[ $cnt -lt 2 ]]; then 142 | echo "ERROR: '${r}()' has only one return value; it should not be reported as having incompatible return values" 143 | fail 144 | fi 145 | return_count=$((return_count+1)) 146 | done 147 | 148 | if [[ -n "$VERBOSE" ]]; then 149 | echo "Found $return_count functions with multiple incompatible return values." 150 | echo "Ok" 151 | echo "Validating skipped functions have incompatible params/counts..." 152 | fi 153 | 154 | param_mismatches=$(awk '/due to param / { print $1 }' $outdir/skipped_fns) 155 | 156 | multiple=0 157 | multiple_inline=0 158 | optimized=0 159 | warnings=0 160 | 161 | for p in $param_mismatches ; do 162 | skipmsg=$(awk -v FN=$p '{ if ($1 == FN) print $0 }' $outdir/skipped_fns) 163 | altname=$(echo $skipmsg | awk '{ i=index($2,")"); print substr($2,2,i-2); }') 164 | if [[ "$altname" != "$p" ]]; then 165 | optimized=$((optimized+1)) 166 | continue 167 | fi 168 | # Ensure there are multiple instances with incompatible params 169 | grep " $p(" $outdir/dwarf.funcs | uniq > ${outdir}/protos.$p 170 | cnt=$(wc -l ${outdir}/protos.$p | awk '{ print $1 }') 171 | if [[ $cnt -lt 2 ]]; then 172 | # function may be inlined in multiple sites with different protos 173 | inlined=$(grep inline ${outdir}/protos.$p) 174 | if [[ -n "$inlined" ]]; then 175 | multiple_inline=$((multiple_inline+1)) 176 | else 177 | if [[ -n "$VERBOSE" ]]; then 178 | echo "WARN: '${p}()' has only one prototype; if it was subject to late optimization, pfunct may not reflect inconsistencies pahole found." 179 | echo "Full skip message from pahole: $skipmsg" 180 | fi 181 | warnings=$((warnings+1)) 182 | fi 183 | else 184 | multiple=$((multiple+1)) 185 | fi 186 | done 187 | 188 | if [[ -n "$VERBOSE" ]]; then 189 | echo "Found $multiple instances with multiple instances with incompatible parameters." 190 | echo "Found $multiple_inline instances where inline functions were not inlined and had incompatible parameters." 191 | echo "Found $optimized instances where the function name suggests optimizations led to inconsistent parameters." 192 | echo "Found $warnings instances where pfunct did not notice inconsistencies." 193 | fi 194 | echo "Ok" 195 | 196 | exit 0 197 | -------------------------------------------------------------------------------- /tests/default_vmlinux_btf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -n "Default BTF on a system without BTF: " 4 | 5 | ulimit -c 0 6 | 7 | # To suppress the "Segmentation fault core dumped" message in bash we 8 | # pipe it to some other command, if it segfaults it will not produce any 9 | # lines and thus we can infer from the number of lines that the segfault 10 | # took place, tricky, but couldn't find any other way to check this 11 | # while suppressing the core dumped message. -acme 12 | 13 | nr_lines=$(PAHOLE_VMLINUX_BTF_FILENAME=foobar pahole -F btf list_head 2>&1 | wc -l) 14 | 15 | if [ $nr_lines -eq 0 ] ; then 16 | echo "FAILED" 17 | exit 1 18 | fi 19 | 20 | # There is also the case where no debugging info is available, be it DWARF of 21 | # BTF and it segfaults when calling just 'pahole', with no args, so check for 22 | # that as well 23 | # 24 | nr_lines=$(PAHOLE_VMLINUX_BTF_FILENAME=foobar pahole 2>&1 | wc -l) 25 | 26 | if [ $nr_lines -eq 0 ] ; then 27 | echo "FAILED" 28 | exit 1 29 | fi 30 | 31 | echo "Ok" 32 | exit 0 33 | -------------------------------------------------------------------------------- /tests/flexible_arrays.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # Match flexible array member info with the per struct final stats. 5 | # 6 | # Arnaldo Carvalho de Melo (C) 2024- 7 | 8 | vmlinux=${vmlinux:-$1} 9 | 10 | if [ -z "$vmlinux" ] ; then 11 | vmlinux=$(pahole --running_kernel_vmlinux) 12 | fi 13 | 14 | if [ ! -f "$vmlinux" ] ; then 15 | echo "$vmlinux file not available, please specify another" 16 | exit 2 17 | fi 18 | 19 | pretty=$(mktemp /tmp/flexible_arrays.data.sh.XXXXXX.c) 20 | 21 | echo -n "Flexible arrays accounting: " 22 | 23 | for struct in $(pahole -F btf --sizes --with_embedded_flexible_array $vmlinux | cut -f1) ; do 24 | pahole $struct $vmlinux > $pretty 25 | 26 | # We need to check for just one tab before the comment as when expanding unnamed 27 | # structs with members with flexible arrays inside another struct we would mess 28 | # up the accounting, see 'pahole fanotify_fid_event' for instance, circa October 2024: 29 | # $ pahole fanotify_fid_event 30 | # struct fanotify_fid_event { 31 | # struct fanotify_event fae; /* 0 48 */ 32 | # __kernel_fsid_t fsid; /* 48 8 */ 33 | # struct { 34 | # struct fanotify_fh object_fh; /* 56 4 */ 35 | # /* XXX last struct has a flexible array */ 36 | # unsigned char _inline_fh_buf[12]; /* 60 12 */ 37 | # }; /* 56 16 */ 38 | 39 | # /* XXX last struct has embedded flexible array(s) */ 40 | # /* size: 72, cachelines: 2, members: 3 */ 41 | # /* flexible array members: middle: 1 */ 42 | # /* last cacheline: 8 bytes */ 43 | # } 44 | 45 | nr_flexible_arrays=$(grep $'^\t/\* XXX last struct has a flexible array' $pretty | wc -l) 46 | nr_embedded_flexible_arrays=$(grep $'^\t/\* XXX last struct.*embedded flexible array' $pretty | wc -l) 47 | stat_nr_flexible_arrays=$(grep "flexible array members:.*end:" $pretty | sed -r 's/.*end: *([[:digit:]]+).*/\1/g') 48 | [ -z "$stat_nr_flexible_arrays" ] && stat_nr_flexible_arrays=0 49 | stat_nr_embedded_flexible_arrays=$(grep "flexible array members:.*middle:" $pretty | sed -r 's/.*middle: *([[:digit:]]+).*/\1/g') 50 | [ -z "$stat_nr_embedded_flexible_arrays" ] && stat_nr_embedded_flexible_arrays=0 51 | test -n "$VERBOSE" && echo "end: $struct: $nr_flexible_arrays $stat_nr_flexible_arrays" 52 | test -n "$VERBOSE" && echo "middle: $struct: $nr_embedded_flexible_arrays $stat_nr_embedded_flexible_arrays" 53 | 54 | if [ "$nr_embedded_flexible_arrays" != "$stat_nr_embedded_flexible_arrays" ] ; then 55 | test -n "$VERBOSE" && printf "struct %s: The number of embedded flexible arrays (%s) doesn't match the number of members marked as such (%s)\n" \ 56 | "$struct" "$stat_nr_embedded_flexible_arrays" "$nr_embedded_flexible_arrays" 57 | test -n "$VERBOSE" && pahole $struct $vmlinux 58 | FAILED=1 59 | fi 60 | 61 | if [ "$nr_flexible_arrays" != "$stat_nr_flexible_arrays" ] ; then 62 | test -n "$VERBOSE" && printf "struct %s: The number of flexible arrays (%s) doesn't match the number of members marked as such (%s)\n" \ 63 | "$struct" "$stat_nr_flexible_arrays" "$nr_flexible_arrays" 64 | test -n "$VERBOSE" && pahole $struct $vmlinux 65 | FAILED=1 66 | fi 67 | 68 | rm -f $pretty 69 | done 70 | 71 | if [ -n "$FAILED" ] ; then 72 | echo "FAILED" 73 | exit 1 74 | fi 75 | 76 | echo "Ok" 77 | exit 0 78 | -------------------------------------------------------------------------------- /tests/pfunct-btf-decl-tags.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | 4 | # Check that pfunct can print btf_decl_tags read from BTF 5 | 6 | tmpobj=$(mktemp /tmp/pfunct-btf-decl-tags.sh.XXXXXX.o) 7 | 8 | cleanup() 9 | { 10 | rm $tmpobj 11 | } 12 | 13 | trap cleanup EXIT 14 | 15 | echo -n "Check that pfunct can print btf_decl_tags read from BTF: " 16 | 17 | CLANG=${CLANG:-clang} 18 | if ! command -v $CLANG > /dev/null; then 19 | echo "Need clang for test $0" 20 | exit 1 21 | fi 22 | 23 | (cat < 4 | # 5 | # Use pahole to pretty print a perf.data file 6 | 7 | # Check if the perf binary is available, if it is from a distro, normally it 8 | # will get the needed DWARF info using libddebuginfod, we'll check if the 9 | # needed types are available, skipping the test and informing the reason. 10 | 11 | echo -n "Pretty printing of files using DWARF type information: " 12 | 13 | perf=$(which perf 2> /dev/null) 14 | if [ -z "$perf" ] ; then 15 | echo "skip: No 'perf' binary available" 16 | exit 2 17 | fi 18 | 19 | perf_lacks_type_info() { 20 | local type_keyword=$1 21 | local type_name=$2 22 | if ! pahole -C $type_name $perf | grep -q "^$type_keyword $type_name {"; then 23 | echo "skip: $perf doesn't have '$type_keyword $type_name' type info" 24 | return 1 25 | fi 26 | return 0 27 | } 28 | 29 | perf_data=$(mktemp /tmp/prettify_perf.data.sh.XXXXXX.perf.data) 30 | 31 | perf_lacks_type_info struct perf_event_header || exit 2 32 | perf_lacks_type_info enum perf_event_type || exit 2 33 | perf_lacks_type_info enum perf_user_event_type || exit 2 34 | 35 | $perf record --quiet -o $perf_data sleep 0.00001 36 | 37 | number_of_filtered_perf_record_metadata() { 38 | local metadata_record=$1 39 | local count=$(pahole -F dwarf -V $perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C "perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type,filter=type==PERF_RECORD_$metadata_record)" --prettify $perf_data | grep ".type = PERF_RECORD_$metadata_record," | wc -l) 40 | echo "$count" 41 | } 42 | 43 | check_expected_number_of_filtered_perf_record_metadata() { 44 | local metadata_record=$1 45 | local expected_records=$2 46 | local nr_records=$(number_of_filtered_perf_record_metadata $metadata_record) 47 | 48 | if [ "$nr_records" != "$expected_records" ] ; then 49 | echo "FAIL: expected $expected_records PERF_RECORD_$metadata_record metadata records, got $nr_records" 50 | return 1; 51 | fi 52 | return 0 53 | } 54 | 55 | check_expected_number_of_filtered_perf_record_metadata COMM 2 || exit 1 56 | check_expected_number_of_filtered_perf_record_metadata EXIT 1 || exit 1 57 | check_expected_number_of_filtered_perf_record_metadata TIME_CONV 1 || exit 1 58 | check_expected_number_of_filtered_perf_record_metadata THREAD_MAP 1 || exit 1 59 | check_expected_number_of_filtered_perf_record_metadata CPU_MAP 1 || exit 1 60 | check_expected_number_of_filtered_perf_record_metadata FINISHED_INIT 1 || exit 1 61 | 62 | # XXX write more tests that look at the events contents, not just for the presence of a known number of them 63 | 64 | echo "Ok" 65 | 66 | rm -f $perf_data 67 | -------------------------------------------------------------------------------- /tests/reproducible_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # Test if BTF generated serially matches reproducible parallel DWARF loading + serial BTF encoding 5 | # Arnaldo Carvalho de Melo (C) 2024- 6 | 7 | vmlinux=${vmlinux:-$1} 8 | 9 | if [ -z "$vmlinux" ] ; then 10 | vmlinux=$(pahole --running_kernel_vmlinux) 11 | fi 12 | 13 | if [ ! -f "$vmlinux" ] ; then 14 | echo "$vmlinux file not available, please specify another" 15 | exit 2 16 | fi 17 | 18 | outdir=$(mktemp -d /tmp/reproducible_build.sh.XXXXXX) 19 | 20 | echo -n "Parallel reproducible DWARF Loading/Serial BTF encoding: " 21 | 22 | test -n "$VERBOSE" && printf "\nserial encoding...\n" 23 | 24 | # This will make pahole and pfunct to skip rust CUs 25 | export PAHOLE_LANG_EXCLUDE=rust 26 | 27 | pahole --btf_features=default --btf_encode_detached=$outdir/vmlinux.btf.serial $vmlinux 28 | bpftool btf dump file $outdir/vmlinux.btf.serial > $outdir/bpftool.output.vmlinux.btf.serial 29 | 30 | nr_proc=$(getconf _NPROCESSORS_ONLN) 31 | 32 | for threads in $(seq $nr_proc) ; do 33 | test -n "$VERBOSE" && echo $threads threads encoding 34 | pahole -j$threads --btf_features=default,reproducible_build --btf_encode_detached=$outdir/vmlinux.btf.parallel.reproducible $vmlinux & 35 | pahole=$! 36 | # HACK: Wait a bit for pahole to start its threads 37 | sleep 0.3s 38 | # PID part to remove ps output headers 39 | nr_threads_started=$(ps -L -C pahole | grep -v PID | wc -l) 40 | ((nr_threads_started -= 1)) # main thread doesn't count, it waits to join 41 | 42 | if [ $threads != $nr_threads_started ] ; then 43 | echo "ERROR: pahole asked to start $threads encoding threads, started $nr_threads_started" 44 | exit 1; 45 | fi 46 | 47 | # ps -L -C pahole | grep -v PID | nl 48 | test -n "$VERBOSE" && echo $nr_threads_started threads started 49 | wait $pahole 50 | rm -f $outdir/bpftool.output.vmlinux.btf.parallel.reproducible 51 | bpftool btf dump file $outdir/vmlinux.btf.parallel.reproducible > $outdir/bpftool.output.vmlinux.btf.parallel.reproducible 52 | test -n "$VERBOSE" && echo "diff from serial encoding:" 53 | diff -u $outdir/bpftool.output.vmlinux.btf.serial $outdir/bpftool.output.vmlinux.btf.parallel.reproducible > $outdir/diff 54 | if [ -s $outdir/diff ] ; then 55 | echo "ERROR: BTF generated from DWARF in parallel is different from the one generated in serial!" 56 | exit 1 57 | fi 58 | test -n "$VERBOSE" && echo ----------------------------- 59 | done 60 | 61 | rm $outdir/* 62 | rmdir $outdir 63 | 64 | echo "Ok" 65 | 66 | exit 0 67 | -------------------------------------------------------------------------------- /tests/tests: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # Copyright © 2024 Red Hat Inc, Arnaldo Carvalho de Melo 4 | 5 | tests_dir=$(dirname $0) 6 | cd $tests_dir 7 | 8 | let status=0 9 | let nr=1 10 | for test in *.sh ; do 11 | printf "%3d: " $nr 12 | ./$test 13 | case $? in 14 | 0) 15 | ;; 16 | 2) 17 | echo "skipping..." 18 | ;; 19 | *) 20 | status=1 21 | ;; 22 | esac 23 | let nr+=1 24 | done 25 | 26 | cd - 27 | 28 | exit $status 29 | --------------------------------------------------------------------------------