diff -Nru bpftrace-0.14.1/build-static.sh bpftrace-0.15.0/build-static.sh --- bpftrace-0.14.1/build-static.sh 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/build-static.sh 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,7 @@ +#!/bin/bash +set -eu +export BASE=ubuntu-glibc +export LLVM_VERSION=12 +export RUN_TESTS=0 +./build-docker-image.sh +docker run --network host --rm -it -u $(id -u):$(id -g) -v $(pwd):$(pwd) -e STATIC_LINKING=ON -e STATIC_LIBC=OFF -e ALLOW_UNSAFE_PROBE=OFF -e VENDOR_GTEST=ON -e RUN_TESTS=${RUN_TESTS} -e EMBED_USE_LLVM=ON bpftrace-builder-${BASE} "$(pwd)/build-static" Release "$@" diff -Nru bpftrace-0.14.1/CHANGELOG.md bpftrace-0.15.0/CHANGELOG.md --- bpftrace-0.14.1/CHANGELOG.md 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/CHANGELOG.md 2022-05-24 10:41:38.000000000 +0000 @@ -6,13 +6,61 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.15.0] 2022-05-24 + +The 0.15.0 release has basic support for LLVM 14 but not all features work yet, see [#2228](https://github.com/iovisor/bpftrace/pull/2228) + +#### Added +- Add option for unconditional hex output + - [#2200](https://github.com/iovisor/bpftrace/pull/2200) +- Add builtin function: `cgroup_path` + - [#2055](https://github.com/iovisor/bpftrace/pull/2055) +- Limit number of generated BPF programs + - [#2141](https://github.com/iovisor/bpftrace/pull/2141) +- Support the octal format specifier (`%o`) in `printf` + - [#2147](https://github.com/iovisor/bpftrace/pull/2147) +- Improve include paths resolution + - [#2149](https://github.com/iovisor/bpftrace/pull/2149) +- Automatic type resolution from DWARF + - [#2034](https://github.com/iovisor/bpftrace/pull/2034) +- Add builtin function: `bswap` + - [#2166](https://github.com/iovisor/bpftrace/pull/2166) +- Print all maps to stdout on `SIGUSR1` + - [#2203](https://github.com/iovisor/bpftrace/pull/2203) + +#### Changed +- Use auto-resolution of library paths for tools + - [#2181](https://github.com/iovisor/bpftrace/pull/2181) +- Improve handling empty attach points + - [#2179](https://github.com/iovisor/bpftrace/pull/2179) + +#### Fixed +- Fix precedence of multiplicative operations + - [#2096](https://github.com/iovisor/bpftrace/pull/2096) +- Fix probe matching for uprobes with absolute address + - [#2138](https://github.com/iovisor/bpftrace/pull/2138) +- Fix tools to work on new kernel versions + - [#2136](https://github.com/iovisor/bpftrace/pull/2136) +- Fix uprobe target resolution + - [#2180](https://github.com/iovisor/bpftrace/pull/2180) +- Fix using wildcards in kfunc + - [#2208](https://github.com/iovisor/bpftrace/pull/2208) +- Improve handling of format strings + - [#2164](https://github.com/iovisor/bpftrace/pull/2164) +- Fix codegen for buf + - [#2217](https://github.com/iovisor/bpftrace/pull/2217) + +#### Tools +- Update biosnoop.bt for kernel >=5.17 + - [#2207](https://github.com/iovisor/bpftrace/pull/2207) + ## [0.14.1] 2021-12-29 #### Fixed - Fix precedence of multiplicative operations - - [#2096](https//github.com/iovisor/bpftrace/pull/2096) + - [#2096](https://github.com/iovisor/bpftrace/pull/2096) ## [0.14.0] 2021-10-22 @@ -35,6 +83,8 @@ - [#1982](https://github.com/iovisor/bpftrace/pull/1982) - Access to uprobe arguments by name - [#1994](https://github.com/iovisor/bpftrace/pull/1994) +- Support variable strings size + - [#2044](https://github.com/iovisor/bpftrace/pull/2044) #### Changed - Prevent LLVM from unrolling loops @@ -68,6 +118,13 @@ - Write new man page for `bpftrace(8)` - [#1711](https://github.com/iovisor/bpftrace/pull/1711) +## [0.13.1] 2021-12-29 + +#### Fixed + +- Fix precedence of multiplicative operations + - [#2096](https://github.com/iovisor/bpftrace/pull/2096) + ## [0.13.0] 2021-07-01 #### Added diff -Nru bpftrace-0.14.1/cmake/embed/embed_clang.cmake bpftrace-0.15.0/cmake/embed/embed_clang.cmake --- bpftrace-0.14.1/cmake/embed/embed_clang.cmake 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/cmake/embed/embed_clang.cmake 2022-05-24 10:41:38.000000000 +0000 @@ -14,9 +14,6 @@ if(${LLVM_VERSION} VERSION_EQUAL "12.0.0") set(CLANG_DOWNLOAD_URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/clang-${LLVM_VERSION}.src.tar.xz") set(CLANG_URL_CHECKSUM "SHA256=e26e452e91d4542da3ebbf404f024d3e1cbf103f4cd110c26bf0a19621cca9ed") -elseif(${LLVM_VERSION} VERSION_EQUAL "8.0.1") - set(CLANG_DOWNLOAD_URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/cfe-${LLVM_VERSION}.src.tar.xz") - set(CLANG_URL_CHECKSUM "SHA256=70effd69f7a8ab249f66b0a68aba8b08af52aa2ab710dfb8a0fba102685b1646") else() message(FATAL_ERROR "No supported LLVM version has been specified with LLVM_VERSION (${EMBED_LLVM_VERSION}), aborting") endif() diff -Nru bpftrace-0.14.1/cmake/embed/embed_llvm.cmake bpftrace-0.15.0/cmake/embed/embed_llvm.cmake --- bpftrace-0.14.1/cmake/embed/embed_llvm.cmake 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/cmake/embed/embed_llvm.cmake 2022-05-24 10:41:38.000000000 +0000 @@ -26,13 +26,6 @@ set(LLVM_VERSION_MINOR "0") set(LLVM_VERSION_PATCH "0") set(LLVM_URL_CHECKSUM "SHA256=49dc47c8697a1a0abd4ee51629a696d7bfe803662f2a7252a3b16fc75f3a8b50") -elseif(${EMBED_LLVM_VERSION} VERSION_EQUAL "8") - set(LLVM_FULL_VERSION "8.0.1") - set(LLVM_VERSION ${LLVM_FULL_VERSION}) - set(LLVM_VERSION_MAJOR "8") - set(LLVM_VERSION_MINOR "0") - set(LLVM_VERSION_PATCH "1") - set(LLVM_URL_CHECKSUM "SHA256=44787a6d02f7140f145e2250d56c9f849334e11f9ae379827510ed72f12b75e7") else() message(FATAL_ERROR "No supported LLVM version has been specified with LLVM_VERSION (${EMBED_LLVM_VERSION}), aborting") endif() @@ -103,13 +96,6 @@ ) -if(${EMBED_LLVM_VERSION} VERSION_EQUAL "8") - set(LLVM_LIBRARY_TARGETS ${LLVM_LIBRARY_TARGETS} - LLVMBPFAsmPrinter - LLVMOptRemarks - ) -endif() - if(${EMBED_LLVM_VERSION} VERSION_EQUAL "12") set(LLVM_LIBRARY_TARGETS ${LLVM_LIBRARY_TARGETS} LLVMBitstreamReader diff -Nru bpftrace-0.14.1/cmake/FindLibBpf.cmake bpftrace-0.15.0/cmake/FindLibBpf.cmake --- bpftrace-0.14.1/cmake/FindLibBpf.cmake 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/cmake/FindLibBpf.cmake 2022-05-24 10:41:38.000000000 +0000 @@ -55,4 +55,29 @@ check_symbol_exists(bpf_link_create "${LIBBPF_INCLUDE_DIRS}/bpf/bpf.h" HAVE_LIBBPF_LINK_CREATE) SET(CMAKE_REQUIRED_DEFINITIONS) SET(CMAKE_REQUIRED_LIBRARIES) + + INCLUDE(CheckCXXSourceCompiles) + SET(CMAKE_REQUIRED_INCLUDES ${LIBBPF_INCLUDE_DIRS}) + SET(CMAKE_REQUIRED_LIBRARIES ${LIBBPF_LIBRARIES} elf z) + CHECK_CXX_SOURCE_COMPILES(" +#include + +int main(void) { + btf__type_cnt(NULL); + return 0; +} +" HAVE_LIBBPF_BTF_TYPE_CNT) + + CHECK_CXX_SOURCE_COMPILES(" +#include + +int main(void) { + const struct btf_dump_opts *opts = (const struct btf_dump_opts*) 1; + + btf_dump__new(NULL, NULL, NULL, opts); + return 0; +} +" HAVE_LIBBPF_BTF_DUMP_NEW_V0_6_0) + SET(CMAKE_REQUIRED_INCLUDES) + SET(CMAKE_REQUIRED_LIBRARIES) endif() diff -Nru bpftrace-0.14.1/CMakeLists-LLVM.txt bpftrace-0.15.0/CMakeLists-LLVM.txt --- bpftrace-0.14.1/CMakeLists-LLVM.txt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/CMakeLists-LLVM.txt 2022-05-24 10:41:38.000000000 +0000 @@ -3,7 +3,7 @@ include(GNUInstallDirs) -set(LLVM_VERSION "8" CACHE STRING "LLVM version to build") +set(LLVM_VERSION "12" CACHE STRING "LLVM version to build") set(STATIC_LINKING ON) set(STATIC_LIBC OFF) diff -Nru bpftrace-0.14.1/CMakeLists.txt bpftrace-0.15.0/CMakeLists.txt --- bpftrace-0.14.1/CMakeLists.txt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/CMakeLists.txt 2022-05-24 10:41:38.000000000 +0000 @@ -3,8 +3,8 @@ # bpftrace version number components. set(bpftrace_VERSION_MAJOR 0) -set(bpftrace_VERSION_MINOR 14) -set(bpftrace_VERSION_PATCH 1) +set(bpftrace_VERSION_MINOR 15) +set(bpftrace_VERSION_PATCH 0) include(GNUInstallDirs) @@ -14,7 +14,7 @@ set(EMBED_USE_LLVM OFF CACHE BOOL "Use a prebuilt embedded LLVM, speeds up the build process") set(EMBED_BUILD_LLVM OFF CACHE BOOL "Build Clang&LLVM static libs as an ExternalProject and link to these instead of system libs.") -set(EMBED_LLVM_VERSION "8" CACHE STRING "Embedded LLVM/Clang version to build and link against.") +set(EMBED_LLVM_VERSION "12" CACHE STRING "Embedded LLVM/Clang version to build and link against.") set(BUILD_ASAN OFF CACHE BOOL "Build bpftrace with -fsanitize=address") set(ENABLE_MAN ON CACHE BOOL "Build man pages") @@ -96,7 +96,7 @@ include_directories(SYSTEM ${LIBELF_INCLUDE_DIRS}) find_package(LibCereal REQUIRED) -include_directories(SYSTEM ${CEREAL_INCLUDE_DIRS}) +include_directories(SYSTEM ${LIBCEREAL_INCLUDE_DIRS}) find_package(BISON REQUIRED) find_package(FLEX REQUIRED) @@ -165,7 +165,7 @@ endif() set(MIN_LLVM_MAJOR 6) - set(MAX_LLVM_MAJOR 14) + set(MAX_LLVM_MAJOR 15) if((${LLVM_VERSION_MAJOR} VERSION_LESS ${MIN_LLVM_MAJOR}) OR (${LLVM_VERSION_MAJOR} VERSION_GREATER ${MAX_LLVM_MAJOR})) message(SEND_ERROR "Unsupported LLVM version found via ${LLVM_INCLUDE_DIRS}: ${LLVM_VERSION_MAJOR}") @@ -274,6 +274,14 @@ endif() endif(LIBBPF_BTF_DUMP_FOUND) +if (HAVE_LIBBPF_BTF_TYPE_CNT) + set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF_BTF_TYPE_CNT) +endif(HAVE_LIBBPF_BTF_TYPE_CNT) + +if (HAVE_LIBBPF_BTF_DUMP_NEW_V0_6_0) + set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF_BTF_DUMP_NEW_V0_6_0) +endif(HAVE_LIBBPF_BTF_DUMP_NEW_V0_6_0) + if (LIBDW_FOUND) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBDW) endif () diff -Nru bpftrace-0.14.1/debian/changelog bpftrace-0.15.0/debian/changelog --- bpftrace-0.14.1/debian/changelog 2022-01-13 08:33:44.000000000 +0000 +++ bpftrace-0.15.0/debian/changelog 2022-05-24 14:37:37.000000000 +0000 @@ -1,3 +1,10 @@ +bpftrace (0.15.0-0+bionic1) bionic; urgency=medium + + * Non-maintainer upload. + * New upstream version 0.15.0 + + -- Filip Chabik Tue, 24 May 2022 16:37:37 +0200 + bpftrace (0.14.1-0+bionic1) bionic; urgency=medium * Non-maintainer upload. diff -Nru bpftrace-0.14.1/debian/rules bpftrace-0.15.0/debian/rules --- bpftrace-0.14.1/debian/rules 2022-01-13 08:33:44.000000000 +0000 +++ bpftrace-0.15.0/debian/rules 2022-05-24 14:37:37.000000000 +0000 @@ -9,6 +9,7 @@ override_dh_auto_install: dh_auto_install rm -rf debian/bpftrace/usr/share/bpftrace/tools/doc + rm -rf debian/bpftrace/usr/share/bpftrace/tools/old # Move binaries to /usr/sbin mkdir -p debian/bpftrace/usr/sbin diff -Nru bpftrace-0.14.1/docker/Dockerfile.focal bpftrace-0.15.0/docker/Dockerfile.focal --- bpftrace-0.14.1/docker/Dockerfile.focal 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/docker/Dockerfile.focal 2022-05-24 10:41:38.000000000 +0000 @@ -43,6 +43,7 @@ systemtap-sdt-dev \ python3 \ xxd +RUN if [ "$LLVM_VERSION" -ge 13 ] ; then apt-get install -y libmlir-${LLVM_VERSION}-dev ; fi RUN ln -s /usr/bin/python3 /usr/bin/python COPY build.sh /build.sh diff -Nru bpftrace-0.14.1/docker/Dockerfile.release bpftrace-0.15.0/docker/Dockerfile.release --- bpftrace-0.14.1/docker/Dockerfile.release 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/docker/Dockerfile.release 2022-05-24 10:41:38.000000000 +0000 @@ -4,7 +4,7 @@ ARG build_dir=build-embedded # Run security updates -RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get upgrade -y && apt-get install -y xz-utils && rm -rf /var/lib/apt/lists/* COPY /$build_dir/src/bpftrace /usr/bin/bpftrace COPY /tools/*.bt /usr/local/bin/ diff -Nru bpftrace-0.14.1/docker/Dockerfile.ubuntu-glibc bpftrace-0.15.0/docker/Dockerfile.ubuntu-glibc --- bpftrace-0.14.1/docker/Dockerfile.ubuntu-glibc 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/docker/Dockerfile.ubuntu-glibc 2022-05-24 10:41:38.000000000 +0000 @@ -1,5 +1,5 @@ ARG BASE="bionic" -ARG LLVM_VERSION="8" +ARG LLVM_VERSION="12" FROM quay.io/iovisor/bpftrace-llvm:${BASE}_${LLVM_VERSION} ARG BASE diff -Nru bpftrace-0.14.1/docs/reference_guide.md bpftrace-0.15.0/docs/reference_guide.md --- bpftrace-0.14.1/docs/reference_guide.md 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/docs/reference_guide.md 2022-05-24 10:41:38.000000000 +0000 @@ -5,7 +5,7 @@ This is a work in progress. If something is missing, check the bpftrace source to see if these docs are just out of date. And if you find something, please file an issue or pull request to update these docs. -Also, please keep these docs as terse as possible to maintain it's brevity (inspired by the 6-page awk +Also, please keep these docs as terse as possible to maintain their brevity (inspired by the 6-page awk summary from page 106 of [v7vol2b.pdf](https://9p.io/7thEdMan/bswv7.html)). Leave longer examples and discussion to other files in /docs, the /tools/\*\_examples.txt files, or blog posts and other articles. @@ -96,6 +96,7 @@ - [26. `uptr()`: Annotate userspace pointer](#26-uptr-annotate-userspace-pointer) - [27. `kptr()`: Annotate kernelspace pointer](#27-kptr-annotate-kernelspace-pointer) - [28. `macaddr()`: Convert MAC address data to text](#28-macaddr-convert-mac-address-data-to-text) + - [29. `cgroup_path()`: Convert cgroup id to cgroup path](#29-cgroup_path-convert-cgroup-id-to-cgroup-path) - [Map Functions](#map-functions) - [1. Builtins](#1-builtins-2) - [2. `count()`: Count](#2-count-count) @@ -111,6 +112,7 @@ - [1. `printf()`: Per-Event Output](#1-printf-per-event-output) - [2. `interval`: Interval Output](#2-interval-interval-output) - [3. `hist()`, `printf()`: Histogram Printing](#3-hist-print-histogram-printing) + - [4. `SIGUSR1`: On-Demand Output](#4-sigusr1-on-demand-output) - [BTF Support](#btf-support) - [Advanced Tools](#advanced-tools) - [Errors](#errors) @@ -163,6 +165,7 @@ BPFTRACE_NO_CPP_DEMANGLE [default: 0] disable C++ symbol demangling BPFTRACE_MAP_KEYS_MAX [default: 4096] max keys in a map BPFTRACE_MAX_PROBES [default: 512] max number of probes bpftrace can attach to + BPFTRACE_MAX_BPF_PROGS [default: 512] max number of generated BPF programs BPFTRACE_CACHE_USER_SYMBOLS [default: auto] enable user symbol cache BPFTRACE_VMLINUX [default: none] vmlinux path used for kernel symbol resolution BPFTRACE_BTF [default: none] BTF file @@ -502,7 +505,7 @@ By default, bpftrace caches the results of symbols resolutions only when ASLR (Address Space Layout Randomization) is disabled. This is because the symbol addresses change with each execution with ASLR. However, disabling caching may incur some performance. Set this env variable to 1 to force bpftrace to -cache. This is fine if only trace one program execution. +cache. This is fine if only tracing one program execution. ### 9.6 `BPFTRACE_VMLINUX` @@ -529,6 +532,14 @@ fast enough. It may be useful to bump the value higher so more events can be queued up. The tradeoff is that bpftrace will use more memory. +### 9.9 `BPFTRACE_MAX_BPF_PROGS` + +Default: 512 + +This is the maximum number of BPF programs (functions) that bpftrace can generate. +The main purpose of this limit is to prevent bpftrace from hanging since generating a lot of probes +takes a lot of resources (and it should not happen often). + ## 10. Clang Environment Variables bpftrace parses header files using libclang, the C interface to Clang. Thus environment variables @@ -1183,8 +1194,7 @@ uprobe: args->NAME ``` -The arguments can be accessed by dereferencing `args` and accessing the argument's `NAME`. Currently, only -simple types (integer, char, bool, enum) and pointers to them are supported. +The arguments can be accessed by dereferencing `args` and accessing the argument's `NAME`. The list of function's arguments can be retrieved using the verbose list option: ``` @@ -1375,7 +1385,7 @@ profile:us:rate ``` -These operating using perf_events (a Linux kernel facility), which is also used by the `perf` command). +These operate using perf_events (a Linux kernel facility, which is also used by the `perf` command). Examples: @@ -1710,11 +1720,11 @@ Kernel: 5.4 -These are eBPF iterator probes, that allows iteration over kernel objects. +These are eBPF iterator probes, that allow iteration over kernel objects. Iterator probe can't be mixed with any other probe, not even other iterator. -Each iterator probe provides set of fields that could be accessed with +Each iterator probe provides set of fields that can be accessed with ctx pointer. User can display set of available fields for iterator via -lv options as described below. @@ -1750,7 +1760,7 @@ bpftrace:1892 7:anon_inode:bpf_iter ``` -You can get list of available functions via list option: +You can get the list of available functions via list option: ``` # bpftrace -l iter:* @@ -2211,13 +2221,14 @@ - `uptr(void *p)` - Annotate as userspace pointer - `kptr(void *p)` - Annotate as kernelspace pointer - `macaddr(char[6] addr)` - Convert MAC address data +- `bswap(uint[8|16|32|64] n)` - Reverse byte order Some of these are asynchronous: the kernel queues the event, but some time later (milliseconds) it is processed in user-space. The asynchronous actions are: `printf()`, `time()`, and `join()`. Both `ksym()` and `usym()`, as well as the variables `kstack` and `ustack`, record addresses synchronously, but then do symbol translation asynchronously. -A selection of these are discussed in the following sections. +A selection of these is discussed in the following sections. ## 2. `printf()`: Printing @@ -2941,7 +2952,7 @@ Attaching 1 probe... 8 -# bpftrace -e 'struct Foo { int x; char c; } BEGIN { printf("%d\n", sizeof(((struct Foo)0).c)); }' +# bpftrace -e 'struct Foo { int x; char c; } BEGIN { printf("%d\n", sizeof(((struct Foo*)0)->c)); }' Attaching 1 probe... 1 @@ -3092,6 +3103,40 @@ ^C ``` +## 29. `cgroup_path`: Convert cgroup id to cgroup path + +Syntax: `cgroup_path(int cgroupid, string filter)` + +Converts the given cgroup id into the corresponding cgroup path for each cgroup hierarchy the id +appears in. Because the conversion is done in user space, the resulting object can only be used for +printing. + +Optionally a string literal may be passed as the second argument to filter cgroup hierarchies to look +in (interpreted as a wildcard expression). + +Example: + +``` +# bpftrace -e 'BEGIN { print(cgroup_path(5386)); }' +Attaching 1 probe... +unified:/user.slice/user-1000.slice/session-3.scope +``` + +## 30. `bswap`: Reverse byte order + +Syntax: `bswap(uint[8|16|32|64] n)` + +Reverses the order of the bytes in integer `n`. In case of 8 bit integers, `n` is returned without being modified. +The return type is an unsigned integer of the same width as `n`. + +Example: + +``` +# bpftrace -e 'BEGIN { $i = (uint32)0x12345678; printf("Reversing byte order of 0x%x ==> 0x%x\n", $i, bswap($i)); }' +Attaching 1 probe... +Reversing byte order of 0x12345678 ==> 0x78563412 +``` + # Map Functions Maps are special BPF data types that can be used to store counts, statistics, and histograms. They are @@ -3311,7 +3356,7 @@ @bytes[sshd]: count 15, average 16384, total 245760 ``` -This stats() function returns three statistics: the count of events, the average for the argument value, +The stats() function returns three statistics: the count of events, the average for the argument value, and the total of the argument value. This is similar to using count(), avg(), and sum(). ## 8. `hist()`: Log2 Histogram @@ -3519,10 +3564,21 @@ [...] ``` +## 4. `SIGUSR1`: On-Demand Output + +Upon receiving a `SIGUSR1` signal, bpftrace will print all maps to the standard output. + +Example: +``` +# bpftrace -e 'kretprobe:vfs_read { @bytes = hist(retval); }' & +# kill -s USR1 $(pidof bpftrace) +``` + # BTF Support If kernel has BTF, kernel types are automatically available and there is no need to include additional headers -to use them. +to use them. To allow users to detect this situation in scripts, the preprocessor macro `BPFTRACE_HAVE_BTF` +is defined if BTF is detected. See tools/ for examples of its usage. Requirements for using BTF: @@ -3590,7 +3646,7 @@ unnecessary: use `pid` instead of `comm`. Use fewer map keys. 1. Split your program over multiple probes. 1. Check the status of the BPF stack limit in Linux (it may be increased in the future, maybe as a - tuneabe). + tuneable). 1. (advanced): Run -d and examine the LLVM IR, and look for ways to optimize src/ast/codegen_llvm.cpp. ## 2. Kernel headers not found @@ -3601,4 +3657,5 @@ /lib/modules/$(uname -r) ``` -The default search directory can be overridden using the environment variable `BPFTRACE_KERNEL_SOURCE`. +The default search directory can be overridden using the environment variable `BPFTRACE_KERNEL_SOURCE`, and +also `BPFTRACE_KERNEL_BUILD` if it is out-of-tree Linux kernel build. diff -Nru bpftrace-0.14.1/.github/workflows/ci.yml bpftrace-0.15.0/.github/workflows/ci.yml --- bpftrace-0.14.1/.github/workflows/ci.yml 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/.github/workflows/ci.yml 2022-05-24 10:41:38.000000000 +0000 @@ -32,6 +32,7 @@ LLVM_VERSION: 9 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON - NAME: LLVM 9 Release @@ -39,6 +40,7 @@ LLVM_VERSION: 9 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON - NAME: LLVM 10 Debug @@ -46,6 +48,7 @@ LLVM_VERSION: 10 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON - NAME: LLVM 10 Release @@ -53,6 +56,7 @@ LLVM_VERSION: 10 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON - NAME: LLVM 10 Clang Debug @@ -62,6 +66,7 @@ CXX: clang++-10 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON - NAME: LLVM 11 Debug @@ -69,6 +74,7 @@ LLVM_VERSION: 11 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON - NAME: LLVM 11 Release @@ -76,6 +82,7 @@ LLVM_VERSION: 11 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON - NAME: LLVM 12 Release @@ -83,6 +90,7 @@ LLVM_VERSION: 12 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON - NAME: LLVM 13 Release @@ -91,6 +99,7 @@ RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size GTEST_FILTER: '-clang_parser.nested_struct_no_type' + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON - NAME: LLVM 12 Release + libbpf @@ -98,6 +107,7 @@ LLVM_VERSION: 12 RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: focal VENDOR_GTEST: ON BUILD_LIBBPF: ON @@ -167,6 +177,8 @@ -e CC="${CC}" -e CXX="${CXX}" -e GTEST_FILTER="$GTEST_FILTER" + -e TOOLS_TEST_DISABLE="$TOOLS_TEST_DISABLE" + -e TOOLS_TEST_OLDVERSION="$TOOLS_TEST_OLDVERSION" bpftrace-builder-$BASE-llvm-$LLVM_VERSION ${PWD}/build-$TYPE-$BASE $TYPE diff -Nru bpftrace-0.14.1/.github/workflows/embedded_llvm.yml bpftrace-0.15.0/.github/workflows/embedded_llvm.yml --- bpftrace-0.14.1/.github/workflows/embedded_llvm.yml 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/.github/workflows/embedded_llvm.yml 2022-05-24 10:41:38.000000000 +0000 @@ -3,11 +3,13 @@ push: paths: - docker/Dockerfile.llvm - - docker/embedded/* + - cmake/embedded/* + - .github/workflows/embedded_llvm.yml pull_request: paths: - docker/Dockerfile.llvm - - docker/embedded/* + - cmake/embedded/* + - .github/workflows/embedded_llvm.yml schedule: - cron: '0 0 1 * *' @@ -16,7 +18,7 @@ runs-on: ubuntu-latest strategy: matrix: - llvm_version: [8,12] + llvm_version: [12] base: [xenial, bionic] steps: - name: Checkout repo diff -Nru bpftrace-0.14.1/.github/workflows/embedded.yml bpftrace-0.15.0/.github/workflows/embedded.yml --- bpftrace-0.14.1/.github/workflows/embedded.yml 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/.github/workflows/embedded.yml 2022-05-24 10:41:38.000000000 +0000 @@ -3,7 +3,7 @@ on: [push, pull_request] jobs: llvm_clang: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest strategy: matrix: env: @@ -16,10 +16,12 @@ EMBED_USE_LLVM: ON RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: builtin.cgroup,probe.kprobe_offset_fail_size + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: bionic DISTRO: ubuntu-glibc VENDOR_GTEST: ON BUILD_LIBBPF: ON + BCC_REF: v0.23.0 - TYPE: Release NAME: vanilla_llvm12+clang+glibc2.23 LLVM_VERSION: 12 @@ -29,11 +31,13 @@ EMBED_USE_LLVM: ON RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: builtin.cgroup,probe.kprobe_offset_fail_size,other.string compare map lookup + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: xenial DISTRO: ubuntu-glibc CMAKE_EXTRA_FLAGS: "-DCMAKE_CXX_FLAGS='-include /usr/local/include/bcc/compat/linux/bpf.h -D__LINUX_BPF_H__'" VENDOR_GTEST: ON BUILD_LIBBPF: ON + BCC_REF: v0.23.0 - TYPE: Debug NAME: alpine LLVM_VERSION: 9 @@ -41,8 +45,9 @@ STATIC_LIBC: ON EMBED_BUILD_LLVM: OFF RUN_ALL_TESTS: 1 - TEST_GROUPS_DISABLE: "tools-parsing-test" RUNTIME_TEST_DISABLE: json-output.join_delim,other.string compare map lookup,probe.kprobe_offset_fail_size,probe.uprobe_library,usdt."usdt probes - attach to fully specified probe of child",usdt."usdt probes - all probes by wildcard and file with child",usdt."usdt probes - attach to probe by wildcard and file with child",usdt."usdt probes - attach to probes by wildcard file with child",usdt."usdt probes - attach to probe on multiple files by wildcard",usdt."usdt probes - attach to probe with probe builtin and args by file with child",usdt."usdt probes - list probes by pid in separate mountns",usdt."usdt sized arguments",usdt."usdt - list probes by file with wildcarded probe type",uprobe."uprobes - list probes by pid; uprobes only",uprobe."uprobes - list probes by pid in separate mount namespace",other.positional pointer arithmetics + TOOLS_TEST_DISABLE: gethostlatency.bt,threadsnoop.bt + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: alpine DISTRO: alpine ALPINE_VERSION: 3.11 @@ -55,6 +60,8 @@ EMBED_BUILD_LLVM: OFF RUN_ALL_TESTS: 1 RUNTIME_TEST_DISABLE: other.string compare map lookup,probe.kprobe_offset_fail_size,probe.uprobe_library,usdt."usdt probes - attach to fully specified probe of child",usdt."usdt probes - all probes by wildcard and file with child",usdt."usdt probes - attach to probe by wildcard and file with child",usdt."usdt probes - attach to probes by wildcard file with child",usdt."usdt probes - attach to probe on multiple files by wildcard",usdt."usdt probes - attach to probe with probe builtin and args by file with child",usdt."usdt probes - list probes by pid in separate mountns",usdt."usdt sized arguments",usdt."usdt - list probes by file with wildcarded probe type",uprobe."uprobes - list probes by pid; uprobes only",uprobe."uprobes - list probes by pid in separate mount namespace",other.positional pointer arithmetics + TOOLS_TEST_DISABLE: gethostlatency.bt,threadsnoop.bt + TOOLS_TEST_OLDVERSION: biosnoop.bt BASE: alpine DISTRO: alpine ALPINE_VERSION: 3.11 @@ -91,6 +98,8 @@ -e RUNTIME_TEST_DISABLE="${RUNTIME_TEST_DISABLE}" -e VENDOR_GTEST="${VENDOR_GTEST}" -e BUILD_LIBBPF="${BUILD_LIBBPF}" + -e TOOLS_TEST_OLDVERSION="$TOOLS_TEST_OLDVERSION" + -e TOOLS_TEST_DISABLE="$TOOLS_TEST_DISABLE" bpftrace-embedded-${{ matrix.env['BASE'] }} $(pwd)/build-embedded ${TYPE} -j`nproc` diff -Nru bpftrace-0.14.1/.gitignore bpftrace-0.15.0/.gitignore --- bpftrace-0.14.1/.gitignore 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/.gitignore 2022-05-24 10:41:38.000000000 +0000 @@ -1,3 +1,4 @@ +/.idea build/ build-*/ tests/runtime/*.pyc diff -Nru bpftrace-0.14.1/INSTALL.md bpftrace-0.15.0/INSTALL.md --- bpftrace-0.14.1/INSTALL.md 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/INSTALL.md 2022-05-24 10:41:38.000000000 +0000 @@ -8,6 +8,8 @@ - [Debian](#debian-package) - [openSUSE](#openSUSE-package) - [CentOS](#CentOS-package) + - [Arch](#arch-package) + - [Alpine](#alpine-package) - [Docker images](#docker-images) - [Copying bpftrace binary docker](#copying-bpftrace-binary-from-docker) - [Kernel headers install](#kernel-headers-install) @@ -103,6 +105,27 @@ A build maintained by @fbs can be found [here](https://github.com/fbs/el7-bpf-specs/blob/master/README.md#repository). +## Arch package + +In Arch Linux, bpftrace is available in the official repositories. +``` +sudo pacman -S bpftrace +``` + +## Alpine package + +bpftrace is available in Alpine's official `community` repository: + +``` +sudo apk add bpftrace +``` + +To install tools and documentation: + +``` +sudo apk add bpftrace-doc bpftrace-tools bpftrace-tools-doc +``` + # Docker images Each push to master will result in a docker image being built and pushed to @@ -257,10 +280,10 @@ systemtap-sdt-dev \ binutils-dev \ libcereal-dev \ - llvm-7-dev \ - llvm-7-runtime \ - libclang-7-dev \ - clang-7 \ + llvm-12-dev \ + llvm-12-runtime \ + libclang-12-dev \ + clang-12 \ libgtest-dev \ libgmock-dev \ asciidoctor diff -Nru bpftrace-0.14.1/man/adoc/bpftrace.adoc bpftrace-0.15.0/man/adoc/bpftrace.adoc --- bpftrace-0.14.1/man/adoc/bpftrace.adoc 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/man/adoc/bpftrace.adoc 2022-05-24 10:41:38.000000000 +0000 @@ -244,6 +244,14 @@ It may be useful to bump the value higher so more events can be queued up. The tradeoff is that bpftrace will use more memory. +=== BPFTRACE_MAX_BPF_PROGS + +Default: 512 + +This is the maximum number of BPF programs (functions) that bpftrace can generate. +The main purpose of this limit is to prevent bpftrace from hanging since generating a lot of probes +takes a lot of resources (and it should not happen often). + //// ! ! @@ -1236,6 +1244,17 @@ *unsafe* functions can have dangerous side effects and should be used with care, the `--unsafe` flag is required for use. +=== bswap + +.variants +* `uint8 bswap(uint8 n)` +* `uint16 bswap(uint16 n)` +* `uint32 bswap(uint32 n)` +* `uint64 bswap(uint64 n)` + +`bswap` reverses the order of the bytes in integer `n`. In case of 8 bit integers, `n` is returned without being modified. +The return type is an unsigned integer of the same width as `n`. + === buf .variants @@ -1249,7 +1268,7 @@ The `buf_t` object returned by `buf` can safely be printed as a hex encoded string with the `%r` format specifier. -Bytes with values >=32 and \<=126 are printed using their ASCII character, other bytes are printed in hex form (e.g. `\x00`). +Bytes with values >=32 and \<=126 are printed using their ASCII character, other bytes are printed in hex form (e.g. `\x00`). The `%rx` format specifier can be used to print everything in hex form, including ASCII characters. ---- i:s:1 { @@ -1287,6 +1306,34 @@ 55f683ee2000-55f683ee3000 rw-p 00000000 00:00 0 ---- +=== cgroup_path + +.variants +* `cgroup_path cgroup_path(int cgroupid, string filter)` + +Convert cgroup id to cgroup path. +This is done asynchronously in userspace when the cgroup_path value is printed, +therefore it can resolve to a different value if the cgroup id gets reassigned. +This also means that the returned value can only be used for printing. + +A string literal may be passed as an optional second argument to filter cgroup +hierarchies in which the cgroup id is looked up by a wildcard expression (cgroup2 +is always represented by "unified", regardless of where it is mounted). + +The currently mounted hierarchy at /sys/fs/cgroup is used to do the lookup. If +the cgroup with the given id isn't present here (e.g. when running in a Docker +container), the cgroup path won't be found (unlike when looking up the cgroup +path of a process via /proc/.../cgroup). + +---- +BEGIN { + $cgroup_path = cgroup_path(3436); + print($cgroup_path); + print($cgroup_path); /* This may print a different path */ + printf("%s %s", $cgroup_path, $cgroup_path); /* This may print two different paths */ +} +---- + === cgroupid .variants diff -Nru bpftrace-0.14.1/man/adoc/CMakeLists.txt bpftrace-0.15.0/man/adoc/CMakeLists.txt --- bpftrace-0.14.1/man/adoc/CMakeLists.txt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/man/adoc/CMakeLists.txt 2022-05-24 10:41:38.000000000 +0000 @@ -1,21 +1,26 @@ find_program(GZIP gzip REQUIRED) -find_program(ASCIIDOCTOR asciidoctor REQUIRED) +find_program(ASCIIDOCTOR asciidoctor) file(GLOB FILES *.adoc) set(GZFILES "") -foreach(FIL ${FILES}) - get_filename_component(NAME ${FIL} NAME_WE) - set(MANPAGE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.8) - set(GZ_MANPAGE_FILE "${MANPAGE_FILE}.gz") +if(NOT "${ASCIIDOCTOR}" STREQUAL "ASCIIDOCTOR-NOTFOUND") + foreach(FIL ${FILES}) + get_filename_component(NAME ${FIL} NAME_WE) + set(MANPAGE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.8) + set(GZ_MANPAGE_FILE "${MANPAGE_FILE}.gz") - add_custom_command(OUTPUT ${MANPAGE_FILE} - COMMAND ${ASCIIDOCTOR} ${FIL} -b manpage -o - > ${MANPAGE_FILE} - DEPENDS ${FIL}) + add_custom_command(OUTPUT ${MANPAGE_FILE} + COMMAND ${ASCIIDOCTOR} ${FIL} -b manpage -o - > ${MANPAGE_FILE} + DEPENDS ${FIL}) - add_custom_command(OUTPUT ${GZ_MANPAGE_FILE} - COMMAND ${GZIP} -c ${MANPAGE_FILE} > ${GZ_MANPAGE_FILE} - DEPENDS ${MANPAGE_FILE}) + add_custom_command(OUTPUT ${GZ_MANPAGE_FILE} + COMMAND ${GZIP} -c ${MANPAGE_FILE} > ${GZ_MANPAGE_FILE} + DEPENDS ${MANPAGE_FILE}) - list(APPEND GZFILES ${GZ_MANPAGE_FILE}) -endforeach() -add_custom_target(adoc_man DEPENDS ${GZFILES}) -install(FILES ${GZFILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man8) + list(APPEND GZFILES ${GZ_MANPAGE_FILE}) + endforeach() + add_custom_target(adoc_man DEPENDS ${GZFILES}) + install(FILES ${GZFILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man8) +else() + message(WARNING "asciidoctor not found, building without bpftrace manpage") + add_custom_target(adoc_man) +endif() diff -Nru bpftrace-0.14.1/man/man8/syscount.8 bpftrace-0.15.0/man/man8/syscount.8 --- bpftrace-0.14.1/man/man8/syscount.8 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/man/man8/syscount.8 2022-05-24 10:41:38.000000000 +0000 @@ -16,7 +16,7 @@ CONFIG_BPF and bpftrace. .SH EXAMPLES .TP -Count all VFS calls until Ctrl-C is hit: +Count system calls until Ctrl-C is hit: # .B syscount.bt .SH OUTPUT diff -Nru bpftrace-0.14.1/README.md bpftrace-0.15.0/README.md --- bpftrace-0.14.1/README.md 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/README.md 2022-05-24 10:41:38.000000000 +0000 @@ -111,6 +111,24 @@ ## Development +### Docker + +For build & test directly in docker + +``` +$ ./build.sh +``` + +For build in docker then test directly on host + +``` +$ ./build-static.sh +$ ./build-static/src/bpftrace +$ ./build-static/tests/bpftrace_test +``` + +### Vagrant + For development and testing a [Vagrantfile](Vagrantfile) is available. Make sure you have the `vbguest` plugin installed, it is required to correctly diff -Nru bpftrace-0.14.1/src/aot/aot.cpp bpftrace-0.15.0/src/aot/aot.cpp --- bpftrace-0.14.1/src/aot/aot.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/aot/aot.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -62,6 +63,7 @@ void serialize_bytecode(const BpfBytecode &bytecode, std::ostream &out) { cereal::BinaryOutputArchive archive(out); + // cereal::JSONOutputArchive archive(out); archive(bytecode); } diff -Nru bpftrace-0.14.1/src/arch/CMakeLists.txt bpftrace-0.15.0/src/arch/CMakeLists.txt --- bpftrace-0.14.1/src/arch/CMakeLists.txt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/arch/CMakeLists.txt 2022-05-24 10:41:38.000000000 +0000 @@ -10,6 +10,8 @@ add_library(arch x86_64.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "mips64") add_library(arch mips64.cpp) +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + add_library(arch riscv64.cpp) else() message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") endif() diff -Nru bpftrace-0.14.1/src/arch/riscv64.cpp bpftrace-0.15.0/src/arch/riscv64.cpp --- bpftrace-0.14.1/src/arch/riscv64.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/src/arch/riscv64.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,112 @@ +#include "arch.h" + +#include +#include + +// SP points to the first argument that is passed on the stack +#define ARG0_STACK 0 + +namespace bpftrace { +namespace arch { + +// clang-format off +static std::array registers = { + "pc", + "ra", + "sp", + "gp", + "tp", + "t0", + "t1", + "t2", + "s0", + "s1", + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "a6", + "a7", + "s2", + "s3", + "s4", + "s5", + "s6", + "s7", + "s8", + "s9", + "s10", + "s11", + "t3", + "t4", + "t5", + "t6", +}; + +static std::array arg_registers = { + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "a6", + "a7", +}; +// clang-format on + +int offset(std::string reg_name) +{ + auto it = find(registers.begin(), registers.end(), reg_name); + if (it == registers.end()) + { + return -1; + } + return distance(registers.begin(), it); +} + +int max_arg() +{ + return arg_registers.size() - 1; +} + +int arg_offset(int arg_num) +{ + return offset(arg_registers.at(arg_num)); +} + +int pc_offset() +{ + return offset("pc"); +} + +int ret_offset() +{ + return offset("a0"); +} + +int sp_offset() +{ + return offset("sp"); +} + +int arg_stack_offset() +{ + return ARG0_STACK / 8; +} + +std::string name() +{ + return std::string("riscv64"); +} + +std::vector invalid_watchpoint_modes() +{ + throw std::runtime_error( + "Watchpoints are not supported on this architecture"); +} + +} // namespace arch +} // namespace bpftrace diff -Nru bpftrace-0.14.1/src/ast/ast.cpp bpftrace-0.15.0/src/ast/ast.cpp --- bpftrace-0.14.1/src/ast/ast.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/ast.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,8 +1,10 @@ -#include "ast.h" -#include "log.h" -#include "visitors.h" +#include "ast/ast.h" + #include +#include "ast/visitors.h" +#include "log.h" + namespace bpftrace { namespace ast { @@ -624,6 +626,18 @@ index_ = other.index_; } +bool Probe::has_ap_of_probetype(ProbeType probe_type) +{ + if (!attach_points) + return false; + for (auto ap : *attach_points) + { + if (probetype(ap->provider) == probe_type) + return true; + } + return false; +} + While::While(const While &other) : Statement(other) { } diff -Nru bpftrace-0.14.1/src/ast/ast.h bpftrace-0.15.0/src/ast/ast.h --- bpftrace-0.14.1/src/ast/ast.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/ast.h 2022-05-24 10:41:38.000000000 +0000 @@ -1,14 +1,14 @@ #pragma once -#include "location.hh" -#include "utils.h" #include #include #include +#include "location.hh" #include "mapkey.h" #include "types.h" #include "usdt.h" +#include "utils.h" namespace bpftrace { namespace ast { @@ -581,6 +581,8 @@ int index() const; void set_index(int index); + bool has_ap_of_probetype(ProbeType probe_type); + private: Probe(const Probe &other); int index_ = 0; diff -Nru bpftrace-0.14.1/src/ast/async_event_types.cpp bpftrace-0.15.0/src/ast/async_event_types.cpp --- bpftrace-0.14.1/src/ast/async_event_types.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/async_event_types.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,9 +1,9 @@ -#include "async_event_types.h" +#include "ast/async_event_types.h" #include #include -#include "irbuilderbpf.h" +#include "ast/irbuilderbpf.h" namespace bpftrace { namespace AsyncEvent { @@ -86,5 +86,13 @@ }; } +std::vector CgroupPath::asLLVMType(ast::IRBuilderBPF& b) +{ + return { + b.getInt64Ty(), // cgroup path (pseudo-event) id + b.getInt64Ty(), // cgroup id + }; +} + } // namespace AsyncEvent } // namespace bpftrace diff -Nru bpftrace-0.14.1/src/ast/async_event_types.h bpftrace-0.15.0/src/ast/async_event_types.h --- bpftrace-0.14.1/src/ast/async_event_types.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/async_event_types.h 2022-05-24 10:41:38.000000000 +0000 @@ -109,5 +109,13 @@ std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); +struct CgroupPath +{ + uint64_t cgroup_path_id; + uint64_t cgroup_id; + + std::vector asLLVMType(ast::IRBuilderBPF& b); +} __attribute__((packed)); + } // namespace AsyncEvent } // namespace bpftrace diff -Nru bpftrace-0.14.1/src/ast/attachpoint_parser.cpp bpftrace-0.15.0/src/ast/attachpoint_parser.cpp --- bpftrace-0.14.1/src/ast/attachpoint_parser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/attachpoint_parser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,4 +1,4 @@ -#include "attachpoint_parser.h" +#include "ast/attachpoint_parser.h" #include #include @@ -7,14 +7,14 @@ #include #include -#include "int_parser.h" -#include "log.h" -#include "types.h" - #ifdef HAVE_BCC_WHICH_SO #include #endif +#include "ast/int_parser.h" +#include "log.h" +#include "types.h" + namespace bpftrace { namespace ast { @@ -86,6 +86,19 @@ } } } + + auto new_end = std::remove_if(probe->attach_points->begin(), + probe->attach_points->end(), + [](const AttachPoint *ap) { + return ap->provider.empty(); + }); + probe->attach_points->erase(new_end, probe->attach_points->end()); + + if (probe->attach_points->empty()) + { + LOG(ERROR, probe->loc, sink_) << "No attach points for probe"; + failed++; + } } return failed; @@ -105,6 +118,13 @@ return INVALID; } + if (parts_.front().empty()) + { + // Do not fail on empty attach point, could be just a trailing comma + ap_->provider = ""; + return OK; + } + std::set probe_types; if (has_wildcard(parts_.front())) { @@ -351,7 +371,8 @@ { // For PID, the target may be skipped parts_.push_back(parts_[1]); - parts_[1] = ""; + auto target = get_pid_exe(bpftrace_.pid()); + parts_[1] = path_for_pid_mountns(bpftrace_.pid(), target); } if (parts_.size() != 3) { @@ -380,13 +401,7 @@ if (ap_->target.empty()) { - if (bpftrace_.pid() > 0) - { - ap_->target = get_pid_exe(bpftrace_.pid()); - ap_->target = path_for_pid_mountns(bpftrace_.pid(), ap_->target); - } - else - ap_->target = parts_[1]; + ap_->target = parts_[1]; } // Handle uprobe:/lib/asdf:func+0x100 case @@ -432,6 +447,11 @@ auto res = stoll(parts_[2]); if (res) { + if (has_wildcard(ap_->target)) + { + errs_ << "Cannot use wildcards with absolute address" << std::endl; + return INVALID; + } ap_->address = *res; } else diff -Nru bpftrace-0.14.1/src/ast/attachpoint_parser.h bpftrace-0.15.0/src/ast/attachpoint_parser.h --- bpftrace-0.14.1/src/ast/attachpoint_parser.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/attachpoint_parser.h 2022-05-24 10:41:38.000000000 +0000 @@ -4,7 +4,7 @@ #include #include -#include "ast.h" +#include "ast/ast.h" #include "bpftrace.h" namespace bpftrace { diff -Nru bpftrace-0.14.1/src/ast/codegen_helper.h bpftrace-0.15.0/src/ast/codegen_helper.h --- bpftrace-0.14.1/src/ast/codegen_helper.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/codegen_helper.h 2022-05-24 10:41:38.000000000 +0000 @@ -1,4 +1,5 @@ #pragma once + #include "bpftrace.h" namespace bpftrace { @@ -6,14 +7,14 @@ inline bool needMemcpy(const SizedType &stype) { - return stype.IsAggregate() || stype.IsTimestampTy(); + return stype.IsAggregate() || stype.IsTimestampTy() || stype.IsCgroupPathTy(); } inline bool shouldBeOnStackAlready(const SizedType &type) { return type.IsStringTy() || type.IsBufferTy() || type.IsInetTy() || type.IsUsymTy() || type.IsTupleTy() || type.IsTimestampTy() || - type.IsMacAddressTy(); + type.IsMacAddressTy() || type.IsCgroupPathTy(); } inline bool onStack(const SizedType &type) diff -Nru bpftrace-0.14.1/src/ast/int_parser.cpp bpftrace-0.15.0/src/ast/int_parser.cpp --- bpftrace-0.14.1/src/ast/int_parser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/int_parser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,3 +1,5 @@ +#include "ast/int_parser.h" + #include #include #include @@ -6,8 +8,6 @@ #include #include -#include "int_parser.h" - namespace { template diff -Nru bpftrace-0.14.1/src/ast/irbuilderbpf.cpp bpftrace-0.15.0/src/ast/irbuilderbpf.cpp --- bpftrace-0.14.1/src/ast/irbuilderbpf.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/irbuilderbpf.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,17 +1,18 @@ +#include "ast/irbuilderbpf.h" + #include #include +#include +#include + #include "arch/arch.h" #include "ast/async_event_types.h" +#include "ast/codegen_helper.h" #include "bpftrace.h" -#include "codegen_helper.h" -#include "irbuilderbpf.h" #include "log.h" #include "utils.h" -#include -#include - namespace libbpf { #undef __BPF_FUNC_MAPPER #include "libbpf/bpf.h" @@ -38,9 +39,9 @@ case libbpf::BPF_FUNC_probe_read_kernel_str: return "probe_read_kernel_str"; default: - throw std::runtime_error("BUG: unknown probe_read id: " + - std::to_string(id)); + LOG(BUG) << "unknown probe_read id: " << std::to_string(id); } + // lgtm[cpp/missing-return] } } // namespace @@ -94,8 +95,8 @@ Value *pid = CreateLShr(CreateGetPidTgid(), 32); // The extra 0 here ensures the type of addr_offset will be int64 - Value *addr_offset = CreateGEP(buf, { getInt64(0), getInt32(0) }); - Value *pid_offset = CreateGEP(buf, { getInt64(0), getInt32(1) }); + Value *addr_offset = CreateGEP(usym_t, buf, { getInt64(0), getInt32(0) }); + Value *pid_offset = CreateGEP(usym_t, buf, { getInt64(0), getInt32(1) }); CreateStore(val, addr_offset); CreateStore(pid, pid_offset); @@ -281,7 +282,7 @@ { #if LLVM_VERSION_MAJOR >= 11 auto *calleePtrType = cast(callee->getType()); - auto *calleeType = cast(calleePtrType->getElementType()); + auto *calleeType = cast(calleePtrType->getPointerElementType()); return CreateCall(calleeType, callee, args, Name); #else return CreateCall(callee, args, Name); @@ -382,7 +383,7 @@ else { assert(value->getType()->isPointerTy() && - (value->getType()->getElementType() == getInt64Ty())); + (value->getType()->getPointerElementType() == getInt64Ty())); // createMapLookup returns an u8* auto *cast = CreatePointerCast(call, value->getType(), "cast"); CreateStore(CreateLoad(getInt64Ty(), cast), value); @@ -401,7 +402,8 @@ if (needMemcpy(type)) return value; - Value *ret = CreateLoad(value); + // value is a pointer to i64 + Value *ret = CreateLoad(getInt64Ty(), value); CreateLifetimeEnd(value); return ret; } @@ -621,7 +623,10 @@ // bpftrace's args are internally represented as 64 bit integers. However, // the underlying argument (of the target program) may be less than 64 // bits. So we must be careful to zero out unused bits. - Value* reg = CreateGEP(ctx, getInt64(offset * sizeof(uintptr_t)), "load_register"); + Value *reg = CreateGEP(getInt8Ty(), + ctx, + getInt64(offset * sizeof(uintptr_t)), + "load_register"); AllocaInst *dst = CreateAllocaBPF(builtin.type, builtin.ident); Value *index_offset = nullptr; if (argument->valid & BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME) @@ -632,7 +637,8 @@ LOG(FATAL) << "offset for register " << argument->index_register_name << " not known"; } - index_offset = CreateGEP(ctx, + index_offset = CreateGEP(getInt8Ty(), + ctx, getInt64(ioffset * sizeof(uintptr_t)), "load_register"); index_offset = CreateLoad(getInt64Ty(), index_offset); @@ -756,19 +762,22 @@ else literal2 = std::nullopt; + auto *val1p = dyn_cast(val1->getType()); + auto *val2p = dyn_cast(val2->getType()); #ifndef NDEBUG if (!literal1) { - PointerType *val1p = cast(val1->getType()); - assert(val1p->getElementType()->isArrayTy() && - val1p->getElementType()->getArrayElementType() == getInt8Ty()); + assert(val1p); + assert(val1p->getPointerElementType()->isArrayTy() && + val1p->getPointerElementType()->getArrayElementType() == + getInt8Ty()); } if (!literal2) { - PointerType *val2p = cast(val2->getType()); - - assert(val2p->getElementType()->isArrayTy() && - val2p->getElementType()->getArrayElementType() == getInt8Ty()); + assert(val2p); + assert(val2p->getPointerElementType()->isArrayTy() && + val2p->getPointerElementType()->getArrayElementType() == + getInt8Ty()); } #endif @@ -798,7 +807,9 @@ l = getInt8(literal1->c_str()[i]); else { - auto *ptr_l = CreateGEP(val1, { getInt32(0), getInt32(i) }); + auto *ptr_l = CreateGEP(val1p->getPointerElementType(), + val1, + { getInt32(0), getInt32(i) }); l = CreateLoad(getInt8Ty(), ptr_l); } @@ -807,7 +818,9 @@ r = getInt8(literal2->c_str()[i]); else { - auto *ptr_r = CreateGEP(val2, { getInt32(0), getInt32(i) }); + auto *ptr_r = CreateGEP(val2p->getPointerElementType(), + val2, + { getInt32(0), getInt32(i) }); r = CreateLoad(getInt8Ty(), ptr_r); } @@ -829,7 +842,8 @@ CreateBr(str_ne); SetInsertPoint(str_ne); - Value *result = CreateLoad(store); + // store is a pointer to bool (i1 *) + Value *result = CreateLoad(getInt1Ty(), store); CreateLifetimeEnd(store); result = CreateIntCast(result, getInt64Ty(), false); @@ -969,9 +983,10 @@ size_t size, const location &loc) { - assert(buf->getType()->getElementType()->isArrayTy() && - buf->getType()->getElementType()->getArrayNumElements() >= size && - buf->getType()->getElementType()->getArrayElementType() == + assert(buf->getType()->getPointerElementType()->isArrayTy() && + buf->getType()->getPointerElementType()->getArrayNumElements() >= + size && + buf->getType()->getPointerElementType()->getArrayElementType() == getInt8Ty()); // int bpf_get_current_comm(char *buf, int size_of_buf) @@ -1056,9 +1071,10 @@ { assert(type.IsIntTy() || type.IsPtrTy()); ctx = CreatePointerCast(ctx, getInt64Ty()->getPointerTo()); - Value *expr = CreateLoad(GetType(type), - CreateGEP(ctx, getInt64(type.funcarg_idx)), - name); + Value *expr = CreateLoad( + GetType(type), + CreateGEP(getInt64Ty(), ctx, getInt64(type.funcarg_idx)), + name); // LLVM 7.0 <= does not have CreateLoad(*Ty, *Ptr, isVolatile, Name), // so call setVolatile() manually @@ -1081,7 +1097,7 @@ // `(uint8*)ctx`, but sometimes this causes invalid context access. // Mark every context access to suppress any LLVM optimization. Value *result = CreateLoad(getInt64Ty(), - CreateGEP(ctx_ptr, getInt64(offset)), + CreateGEP(getInt64Ty(), ctx_ptr, getInt64(offset)), builtin); // LLVM 7.0 <= does not have CreateLoad(*Ty, *Ptr, isVolatile, Name), // so call setVolatile() manually @@ -1134,12 +1150,15 @@ elements, true); AllocaInst *buf = CreateAllocaBPF(helper_error_struct, "helper_error_t"); - CreateStore(GetIntSameSize(asyncactionint(AsyncAction::helper_error), - elements.at(0)), - CreateGEP(buf, { getInt64(0), getInt32(0) })); - CreateStore(GetIntSameSize(error_id, elements.at(1)), - CreateGEP(buf, { getInt64(0), getInt32(1) })); - CreateStore(return_value, CreateGEP(buf, { getInt64(0), getInt32(2) })); + CreateStore( + GetIntSameSize(asyncactionint(AsyncAction::helper_error), elements.at(0)), + CreateGEP(helper_error_struct, buf, { getInt64(0), getInt32(0) })); + CreateStore( + GetIntSameSize(error_id, elements.at(1)), + CreateGEP(helper_error_struct, buf, { getInt64(0), getInt32(1) })); + CreateStore( + return_value, + CreateGEP(helper_error_struct, buf, { getInt64(0), getInt32(2) })); auto &layout = module_.getDataLayout(); auto struct_size = layout.getTypeAllocSize(helper_error_struct); @@ -1229,11 +1248,13 @@ ctx = CreatePointerCast(ctx, getInt8Ty()->getPointerTo()); Value *meta = CreateLoad(getInt64Ty()->getPointerTo(), - CreateGEP(ctx, getInt64(0)), + CreateGEP(getInt8Ty(), ctx, getInt64(0)), "meta"); dyn_cast(meta)->setVolatile(true); - Value *seq = CreateLoad(getInt64Ty(), CreateGEP(meta, getInt64(0)), "seq"); + Value *seq = CreateLoad(getInt64Ty(), + CreateGEP(getInt64Ty(), meta, getInt64(0)), + "seq"); CallInst *call = createCall(seq_printf_func, { seq, fmt, fmt_size, data, data_len }, diff -Nru bpftrace-0.14.1/src/ast/irbuilderbpf.h bpftrace-0.15.0/src/ast/irbuilderbpf.h --- bpftrace-0.14.1/src/ast/irbuilderbpf.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/irbuilderbpf.h 2022-05-24 10:41:38.000000000 +0000 @@ -1,13 +1,14 @@ #pragma once -#include "ast.h" -#include "bpftrace.h" -#include "types.h" #include #include #include +#include "ast/ast.h" +#include "bpftrace.h" +#include "types.h" + #if LLVM_VERSION_MAJOR >= 5 && LLVM_VERSION_MAJOR < 7 #define CREATE_MEMCPY(dst, src, size, algn) \ CreateMemCpy((dst), (src), (size), (algn)) diff -Nru bpftrace-0.14.1/src/ast/passes/callback_visitor.h bpftrace-0.15.0/src/ast/passes/callback_visitor.h --- bpftrace-0.14.1/src/ast/passes/callback_visitor.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/callback_visitor.h 2022-05-24 10:41:38.000000000 +0000 @@ -1,8 +1,9 @@ #pragma once -#include "visitors.h" #include +#include "ast/visitors.h" + namespace bpftrace { namespace ast { diff -Nru bpftrace-0.14.1/src/ast/passes/codegen_llvm.cpp bpftrace-0.15.0/src/ast/passes/codegen_llvm.cpp --- bpftrace-0.14.1/src/ast/passes/codegen_llvm.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/codegen_llvm.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,14 +1,5 @@ #include "codegen_llvm.h" -#include "arch/arch.h" -#include "ast.h" -#include "ast/async_event_types.h" -#include "ast/bpforc/bpforc.h" -#include "codegen_helper.h" -#include "log.h" -#include "signal_bt.h" -#include "tracepoint_format_parser.h" -#include "types.h" -#include "usdt.h" + #include #include #include @@ -21,9 +12,23 @@ #include #include #include +#if LLVM_VERSION_MAJOR >= 14 +#include +#endif #include #include +#include "arch/arch.h" +#include "ast.h" +#include "ast/async_event_types.h" +#include "ast/bpforc/bpforc.h" +#include "ast/codegen_helper.h" +#include "ast/signal_bt.h" +#include "log.h" +#include "tracepoint_format_parser.h" +#include "types.h" +#include "usdt.h" + namespace bpftrace { namespace ast { @@ -233,16 +238,17 @@ int arg_num = atoi(builtin.ident.substr(4).c_str()); Value *ctx = b_.CreatePointerCast(ctx_, b_.getInt64Ty()->getPointerTo()); - Value *sp = b_.CreateLoad(b_.getInt64Ty(), - b_.CreateGEP(ctx, b_.getInt64(sp_offset)), - "reg_sp"); + Value *sp = b_.CreateLoad( + b_.getInt64Ty(), + b_.CreateGEP(b_.getInt64Ty(), ctx, b_.getInt64(sp_offset)), + "reg_sp"); dyn_cast(sp)->setVolatile(true); AllocaInst *dst = b_.CreateAllocaBPF(builtin.type, builtin.ident); Value *src = b_.CreateAdd(sp, b_.getInt64((arg_num + arch::arg_stack_offset()) * sizeof(uintptr_t))); b_.CreateProbeRead(ctx_, dst, 8, src, builtin.type.GetAS(), builtin.loc); - expr_ = b_.CreateLoad(dst); + expr_ = b_.CreateLoad(b_.GetType(builtin.type), dst); b_.CreateLifetimeEnd(dst); } else if (builtin.ident == "probe") @@ -267,7 +273,7 @@ { pid_t cpid = bpftrace_.child_->pid(); if (cpid < 1) { - LOG(FATAL) << "BUG: Invalid cpid: " << cpid; + LOG(BUG) << "Invalid cpid: " << cpid; } expr_ = b_.getInt64(cpid); } @@ -510,8 +516,12 @@ b_.CREATE_MEMSET(buf, b_.getInt8(0), bpftrace_.strlen_, 1); auto arg0 = call.vargs->front(); auto scoped_del = accept(call.vargs->front()); - b_.CreateProbeReadStr( - ctx_, buf, b_.CreateLoad(strlen), expr_, arg0->type.GetAS(), call.loc); + b_.CreateProbeReadStr(ctx_, + buf, + b_.CreateLoad(b_.getInt64Ty(), strlen), + expr_, + arg0->type.GetAS(), + call.loc); b_.CreateLifetimeEnd(strlen); expr_ = buf; @@ -529,6 +539,8 @@ auto scoped_del = accept(&arg); Value *proposed_length = expr_; + if (arg.type.GetSize() != 8) + proposed_length = b_.CreateZExt(proposed_length, max_length->getType()); Value *cmp = b_.CreateICmp( CmpInst::ICMP_ULE, proposed_length, max_length, "length.cmp"); length = b_.CreateSelect( @@ -553,12 +565,14 @@ false); AllocaInst *buf = b_.CreateAllocaBPF(buf_struct, "buffer"); - Value *buf_len_offset = b_.CreateGEP(buf, + Value *buf_len_offset = b_.CreateGEP(buf_struct, + buf, { b_.getInt32(0), b_.getInt32(0) }); length = b_.CreateIntCast(length, buf_struct->getElementType(0), false); b_.CreateStore(length, buf_len_offset); - Value *buf_data_offset = b_.CreateGEP(buf, + Value *buf_data_offset = b_.CreateGEP(buf_struct, + buf, { b_.getInt32(0), b_.getInt32(1) }); b_.CREATE_MEMSET(buf_data_offset, b_.GetIntSameSize(0, elements.at(0)), @@ -567,16 +581,19 @@ auto scoped_del = accept(call.vargs->front()); auto arg0 = call.vargs->front(); - // arg0 is already on the bpf stack -> use probe kernel - // otherwise -> addrspace of arg0->type + // arg0 is already on the bpf stack -> use memcpy + // otherwise -> probe read in addrspace of arg0->type // case : struct MyStruct { char b[4]; }; // $s = (struct MyStruct *)arg0; buf($s->b, 4) - b_.CreateProbeRead(ctx_, - static_cast(buf_data_offset), - length, - expr_, - find_addrspace_stack(arg0->type), - call.loc); + if (shouldBeOnStackAlready(arg0->type)) + b_.CREATE_MEMCPY(buf_data_offset, expr_, length, 1); + else + b_.CreateProbeRead(ctx_, + static_cast(buf_data_offset), + length, + expr_, + find_addrspace_stack(arg0->type), + call.loc); expr_ = buf; expr_deleter_ = [this, buf]() { b_.CreateLifetimeEnd(buf); }; @@ -648,14 +665,14 @@ b_.SetInsertPoint(notzero); b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::join)), perfdata); b_.CreateStore(b_.getInt64(join_id_), - b_.CreateGEP(perfdata, b_.getInt64(8))); + b_.CreateGEP(b_.getInt8Ty(), perfdata, b_.getInt64(8))); join_id_++; AllocaInst *arr = b_.CreateAllocaBPF(b_.getInt64Ty(), call.func + "_r0"); b_.CreateProbeRead(ctx_, arr, 8, expr_, addrspace, call.loc); b_.CreateProbeReadStr(ctx_, b_.CreateAdd(perfdata, b_.getInt64(8 + 8)), bpftrace_.join_argsize_, - b_.CreateLoad(arr), + b_.CreateLoad(b_.getInt64Ty(), arr), addrspace, call.loc); @@ -663,14 +680,18 @@ { // argi b_.CreateStore(b_.CreateAdd(expr_, b_.getInt64(8 * i)), first); - b_.CreateProbeRead( - ctx_, second, 8, b_.CreateLoad(first), addrspace, call.loc); + b_.CreateProbeRead(ctx_, + second, + 8, + b_.CreateLoad(b_.getInt64Ty(), first), + addrspace, + call.loc); b_.CreateProbeReadStr( ctx_, b_.CreateAdd(perfdata, b_.getInt64(8 + 8 + i * bpftrace_.join_argsize_)), bpftrace_.join_argsize_, - b_.CreateLoad(second), + b_.CreateLoad(b_.getInt64Ty(), second), addrspace, call.loc); } @@ -713,7 +734,9 @@ AllocaInst *buf = b_.CreateAllocaBPF(inet_struct, "inet"); - Value *af_offset = b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) }); + Value *af_offset = b_.CreateGEP(inet_struct, + buf, + { b_.getInt64(0), b_.getInt32(0) }); Value *af_type; auto inet = call.vargs->at(0); @@ -736,7 +759,9 @@ } b_.CreateStore(af_type, af_offset); - Value *inet_offset = b_.CreateGEP(buf, {b_.getInt32(0), b_.getInt32(1)}); + Value *inet_offset = b_.CreateGEP(inet_struct, + buf, + { b_.getInt32(0), b_.getInt32(1) }); b_.CREATE_MEMSET(inet_offset, b_.getInt8(0), 16, 1); auto scoped_del = accept(inet); @@ -769,9 +794,10 @@ } Value *ctx = b_.CreatePointerCast(ctx_, b_.getInt64Ty()->getPointerTo()); - expr_ = b_.CreateLoad(b_.getInt64Ty(), - b_.CreateGEP(ctx, b_.getInt64(offset)), - call.func + "_" + reg_name); + expr_ = b_.CreateLoad( + b_.getInt64Ty(), + b_.CreateGEP(b_.getInt64Ty(), ctx, b_.getInt64(offset)), + call.func + "_" + reg_name); dyn_cast(expr_)->setVolatile(true); } else if (call.func == "printf") @@ -796,8 +822,10 @@ auto scoped_del = accept(&arg); // and store it to data area - Value *offset = b_.CreateGEP( - data, { b_.getInt64(0), b_.getInt64((i - 1) * ptr_size) }); + Value *offset = b_.CreateGEP(b_.GetType(data_type), + data, + { b_.getInt64(0), + b_.getInt64((i - 1) * ptr_size) }); b_.CreateStore(expr_, offset); // keep the expression alive, so it's still there @@ -872,6 +900,32 @@ else createPrintNonMapCall(call, non_map_print_id_); } + else if (call.func == "cgroup_path") + { + auto elements = AsyncEvent::CgroupPath().asLLVMType(b_); + StructType *cgroup_path_struct = b_.GetStructType(call.func + "_t", + elements, + true); + AllocaInst *buf = b_.CreateAllocaBPF(cgroup_path_struct, + call.func + "_args"); + + // Store cgroup path event id + b_.CreateStore(b_.GetIntSameSize(cgroup_path_id_, elements.at(0)), + b_.CreateGEP(cgroup_path_struct, + buf, + { b_.getInt64(0), b_.getInt32(0) })); + cgroup_path_id_++; + + // Store cgroup id + auto arg = call.vargs->at(0); + auto scoped_del = accept(arg); + b_.CreateStore(expr_, + b_.CreateGEP(cgroup_path_struct, + buf, + { b_.getInt64(0), b_.getInt32(1) })); + + expr_ = buf; + } else if (call.func == "clear" || call.func == "zero") { auto elements = AsyncEvent::MapEvent().asLLVMType(b_); @@ -885,7 +939,9 @@ AllocaInst *buf = b_.CreateAllocaBPF(event_struct, call.func + "_" + map.ident); - auto aa_ptr = b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) }); + auto aa_ptr = b_.CreateGEP(event_struct, + buf, + { b_.getInt64(0), b_.getInt32(0) }); if (call.func == "clear") b_.CreateStore(b_.GetIntSameSize(asyncactionint(AsyncAction::clear), elements.at(0)), @@ -896,7 +952,9 @@ aa_ptr); auto id = bpftrace_.maps[map.ident].value()->id; - auto *ident_ptr = b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) }); + auto *ident_ptr = b_.CreateGEP(event_struct, + buf, + { b_.getInt64(0), b_.getInt32(1) }); b_.CreateStore(b_.GetIntSameSize(id, elements.at(1)), ident_ptr); b_.CreatePerfEventOutput(ctx_, buf, getStructSize(event_struct)); @@ -912,12 +970,13 @@ AllocaInst *buf = b_.CreateAllocaBPF(time_struct, call.func + "_t"); - b_.CreateStore(b_.GetIntSameSize(asyncactionint(AsyncAction::time), - elements.at(0)), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); + b_.CreateStore( + b_.GetIntSameSize(asyncactionint(AsyncAction::time), elements.at(0)), + b_.CreateGEP(time_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); - b_.CreateStore(b_.GetIntSameSize(time_id_, elements.at(1)), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); + b_.CreateStore( + b_.GetIntSameSize(time_id_, elements.at(1)), + b_.CreateGEP(time_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); time_id_++; b_.CreatePerfEventOutput(ctx_, buf, getStructSize(time_struct)); @@ -932,13 +991,15 @@ true); AllocaInst *buf = b_.CreateAllocaBPF(strftime_struct, call.func + "_args"); - b_.CreateStore(b_.GetIntSameSize(strftime_id_, elements.at(0)), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); + b_.CreateStore( + b_.GetIntSameSize(strftime_id_, elements.at(0)), + b_.CreateGEP(strftime_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); strftime_id_++; Expression *arg = call.vargs->at(1); auto scoped_del = accept(arg); - b_.CreateStore(expr_, - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); + b_.CreateStore( + expr_, + b_.CreateGEP(strftime_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); expr_ = buf; } else if (call.func == "kstack" || call.func == "ustack") @@ -954,7 +1015,7 @@ int sigid = signal_name_to_num(signame); // Should be caught in semantic analyser if (sigid < 1) { - LOG(FATAL) << "BUG: Invalid signal ID for \"" << signame << "\""; + LOG(BUG) << "Invalid signal ID for \"" << signame << "\""; } b_.CreateSignal(ctx_, b_.getInt32(sigid), call.loc); return; @@ -1022,15 +1083,32 @@ AllocaInst *buf = b_.CreateAllocaBPF(unwatch_struct, "unwatch"); size_t struct_size = datalayout().getTypeAllocSize(unwatch_struct); - b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::watchpoint_detach)), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); + b_.CreateStore( + b_.getInt64(asyncactionint(AsyncAction::watchpoint_detach)), + b_.CreateGEP(unwatch_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore( b_.CreateIntCast(expr_, b_.getInt64Ty(), false /* unsigned */), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); + b_.CreateGEP(unwatch_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); b_.CreatePerfEventOutput(ctx_, buf, struct_size); b_.CreateLifetimeEnd(buf); expr_ = nullptr; } + else if (call.func == "bswap") + { + bpftrace::ast::Expression *arg = call.vargs->at(0); + auto scoped_del_arg = accept(arg); + + assert(arg->type.IsIntegerTy()); + if (arg->type.GetSize() > 1) + { + llvm::Type *arg_type = b_.GetType(arg->type); + Function *swap_fun = Intrinsic::getDeclaration(module_.get(), + Intrinsic::bswap, + { arg_type }); + + expr_ = b_.CreateCall(swap_fun, { expr_ }); + } + } else { LOG(FATAL) << "missing codegen for function \"" << call.func << "\""; @@ -1056,7 +1134,9 @@ } else { - expr_ = b_.CreateLoad(variables_[var.ident]); + auto *var_alloca = variables_[var.ident]; + expr_ = b_.CreateLoad(var_alloca->getType()->getPointerElementType(), + var_alloca); } } @@ -1211,7 +1291,7 @@ expr_ = b_.CreateXor(lhs, rhs); break; default: - LOG(FATAL) << "BUG: \"" << opstr(binop) << "\" was handled earlier"; + LOG(BUG) << "\"" << opstr(binop) << "\" was handled earlier"; } // Using signed extension will result in -1 which will likely confuse users expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), false); @@ -1241,15 +1321,15 @@ case Operator::BXOR: case Operator::MUL: case Operator::DIV: - LOG(FATAL) << "BUG: binop_ptr: op not implemented for type\"" - << opstr(binop) << "\""; + LOG(BUG) << "binop_ptr: op not implemented for type\"" << opstr(binop) + << "\""; break; case Operator::PLUS: case Operator::MINUS: arith = true; break; default: - LOG(FATAL) << "BUG: binop_ptr invalid op \"" << opstr(binop) << "\""; + LOG(BUG) << "binop_ptr invalid op \"" << opstr(binop) << "\""; } auto scoped_del_left = accept(binop.left); @@ -1285,7 +1365,7 @@ break; } default: - LOG(FATAL) << "BUG: invalid op \"" << opstr(binop) << "\""; + LOG(BUG) << "invalid op \"" << opstr(binop) << "\""; } } else if (arith) @@ -1379,16 +1459,17 @@ int size = type.GetSize(); auto as = type.GetAS(); - AllocaInst *dst = b_.CreateAllocaBPF(SizedType(type.type, size), "deref"); + auto dst_type = SizedType(type.type, size); + AllocaInst *dst = b_.CreateAllocaBPF(dst_type, "deref"); b_.CreateProbeRead(ctx_, dst, size, expr_, as, unop.loc); - expr_ = b_.CreateIntCast(b_.CreateLoad(dst), + expr_ = b_.CreateIntCast(b_.CreateLoad(b_.GetType(dst_type), dst), b_.getInt64Ty(), type.IsSigned()); b_.CreateLifetimeEnd(dst); break; } default: - LOG(FATAL) << "BUG: unop_int: invalid op \"" << opstr(unop) << "\""; + LOG(BUG) << "unop_int: invalid op \"" << opstr(unop) << "\""; } } @@ -1405,7 +1486,7 @@ int size = unop.type.IsIntegerTy() ? et->GetIntBitWidth() / 8 : 8; AllocaInst *dst = b_.CreateAllocaBPF(*et, "deref"); b_.CreateProbeRead(ctx_, dst, size, expr_, type.GetAS(), unop.loc); - expr_ = b_.CreateIntCast(b_.CreateLoad(dst), + expr_ = b_.CreateIntCast(b_.CreateLoad(b_.GetType(*et), dst), b_.getInt64Ty(), unop.type.IsSigned()); b_.CreateLifetimeEnd(dst); @@ -1484,7 +1565,7 @@ b_.CreateBr(done); b_.SetInsertPoint(done); - expr_ = b_.CreateLoad(result); + expr_ = b_.CreateLoad(b_.GetType(ternary.type), result); } else if (ternary.type.IsStringTy()) { @@ -1547,7 +1628,8 @@ } else if (type.IsTupleTy()) { - Value *src = b_.CreateGEP(expr_, + Value *src = b_.CreateGEP(b_.GetType(type), + expr_, { b_.getInt32(0), b_.getInt32(acc.index) }); SizedType &elem_type = type.GetFields()[acc.index].type; @@ -1594,10 +1676,12 @@ if (field.is_bitfield) { Value *raw; + auto field_type = b_.GetType(field.type); if (type.IsCtxAccess()) - raw = b_.CreateLoad( - b_.CreateIntToPtr(src, b_.GetType(field.type)->getPointerTo()), - true); + raw = b_.CreateLoad(field_type, + b_.CreateIntToPtr(src, + field_type->getPointerTo()), + true); else { AllocaInst *dst = b_.CreateAllocaBPF(field.type, @@ -1608,7 +1692,7 @@ b_.CREATE_MEMSET(dst, b_.getInt8(0), field.type.GetSize(), 1); b_.CreateProbeRead( ctx_, dst, field.bitfield.read_bytes, src, type.GetAS(), acc.loc); - raw = b_.CreateLoad(dst); + raw = b_.CreateLoad(field_type, dst); b_.CreateLifetimeEnd(dst); } size_t rshiftbits; @@ -1636,7 +1720,8 @@ // offset which we add to the start of the tracepoint struct. expr_ = b_.CreateLoad( b_.getInt32Ty(), - b_.CreateGEP(b_.CreatePointerCast(ctx_, + b_.CreateGEP(b_.getInt32Ty(), + b_.CreatePointerCast(ctx_, b_.getInt32Ty()->getPointerTo()), b_.getInt64(field.offset / 4))); expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), false); @@ -1717,8 +1802,8 @@ if (llvm_size != our_size) { - LOG(FATAL) << "BUG: Struct size mismatch: expected: " << our_size - << ", real: " << llvm_size; + LOG(BUG) << "Struct size mismatch: expected: " << our_size + << ", real: " << llvm_size; } auto *layout = datalayout().getStructLayout( @@ -1755,7 +1840,9 @@ Expression *elem = tuple.elems->at(i); auto scoped_del = accept(elem); - Value *dst = b_.CreateGEP(buf, { b_.getInt32(0), b_.getInt32(i) }); + Value *dst = b_.CreateGEP(tuple_ty, + buf, + { b_.getInt32(0), b_.getInt32(i) }); if (onStack(elem->type)) b_.CREATE_MEMCPY(dst, expr_, elem->type.GetSize(), 1); @@ -1793,7 +1880,17 @@ auto [key, scoped_key_deleter] = getMapKey(map); if (shouldBeOnStackAlready(assignment.expr->type)) { - val = expr; + if ((assignment.expr->type.IsStringTy() || + assignment.expr->type.IsTupleTy()) && + assignment.expr->type.GetSize() != map.type.GetSize()) + { + val = b_.CreateAllocaBPF(map.type, map.ident + "_val"); + b_.CREATE_MEMSET(val, b_.getInt8(0), map.type.GetSize(), 1); + b_.CREATE_MEMCPY(val, expr, assignment.expr->type.GetSize(), 1); + self_alloca = true; + } + else + val = expr; } else if (map.type.IsRecordTy() || map.type.IsArrayTy()) { @@ -1875,7 +1972,8 @@ } else if (needMemcpy(var.type)) { - b_.CREATE_MEMCPY(variables_[var.ident], expr_, var.type.GetSize(), 1); + b_.CREATE_MEMCPY( + variables_[var.ident], expr_, assignment.expr->type.GetSize(), 1); } else { @@ -1970,7 +2068,7 @@ b_.CreateBr(std::get<0>(loops_.back())); break; default: - LOG(FATAL) << "BUG: jump: invalid op \"" << opstr(jump) << "\""; + LOG(BUG) << "jump: invalid op \"" << opstr(jump) << "\""; } // LLVM doesn't like having instructions after an unconditional branch (segv) @@ -2205,6 +2303,18 @@ matches = bpftrace_.probe_matcher_->get_matches_for_ap(*attach_point); } + probe_count_ += matches.size(); + if (probe_count_ > bpftrace_.max_programs_) + { + throw std::runtime_error( + "Your program is trying to generate more than " + + std::to_string(probe_count_) + + " BPF programs, which exceeds the current limit of " + + std::to_string(bpftrace_.max_programs_) + + ".\nYou can increase the limit through the BPFTRACE_MAX_BPF_PROGS " + "environment variable."); + } + tracepoint_struct_ = ""; for (const auto &m : matches) { @@ -2329,9 +2439,20 @@ auto scoped_del = accept(expr); if (onStack(expr->type)) { - key = expr_; - // Call-ee freed - scoped_del.disarm(); + auto &key_type = map.key_type.args_[0]; + if (expr->type.IsStringTy() && + expr->type.GetSize() != key_type.GetSize()) + { + key = b_.CreateAllocaBPF(key_type, map.ident + "_key"); + b_.CREATE_MEMSET(key, b_.getInt8(0), key_type.GetSize(), 1); + b_.CREATE_MEMCPY(key, expr_, expr->type.GetSize(), 1); + } + else + { + key = expr_; + // Call-ee freed + scoped_del.disarm(); + } } else { @@ -2379,23 +2500,29 @@ size_t extra_keys_size) { size_t size = extra_keys_size; - for (Expression *expr : *map.vargs) + for (auto &key_type : map.key_type.args_) { - size += expr->type.GetSize(); + size += key_type.GetSize(); } AllocaInst *key = b_.CreateAllocaBPF(size, map.ident + "_key"); + auto *key_type = ArrayType::get(b_.getInt8Ty(), size); int offset = 0; bool aligned = true; + int i = 0; // Construct a map key in the stack for (Expression *expr : *map.vargs) { auto scoped_del = accept(expr); - Value *offset_val = b_.CreateGEP(key, + Value *offset_val = b_.CreateGEP(key_type, + key, { b_.getInt64(0), b_.getInt64(offset) }); + size_t map_key_size = map.key_type.args_[i++].GetSize(); if (onStack(expr->type)) { + if (expr->type.IsStringTy() && expr->type.GetSize() < map_key_size) + b_.CREATE_MEMSET(offset_val, b_.getInt8(0), map_key_size, 1); b_.CREATE_MEMCPY(offset_val, expr_, expr->type.GetSize(), 1); if ((expr->type.GetSize() % 8) != 0) aligned = false; @@ -2428,12 +2555,13 @@ b_.createAlignedStore(key_elem, dst_ptr, 1); } } - offset += expr->type.GetSize(); + offset += map_key_size; } for (auto *extra_key : extra_keys) { - Value *offset_val = b_.CreateGEP(key, + Value *offset_val = b_.CreateGEP(key_type, + key, { b_.getInt64(0), b_.getInt64(offset) }); if (aligned) b_.CreateStore(extra_key, offset_val); @@ -2490,7 +2618,7 @@ b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); - return b_.CreateLoad(result); + return b_.CreateLoad(b_.getInt64Ty(), result); } Value *CodegenLLVM::createLogicalOr(Binop &binop) @@ -2529,7 +2657,7 @@ b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); - return b_.CreateLoad(result); + return b_.CreateLoad(b_.getInt64Ty(), result); } Function *CodegenLLVM::createLog2Function() @@ -2573,34 +2701,37 @@ // test for less than zero BasicBlock *is_less_than_zero = BasicBlock::Create(module_->getContext(), "hist.is_less_than_zero", log2_func); BasicBlock *is_not_less_than_zero = BasicBlock::Create(module_->getContext(), "hist.is_not_less_than_zero", log2_func); - b_.CreateCondBr(b_.CreateICmpSLT(b_.CreateLoad(n_alloc), b_.getInt64(0)), + b_.CreateCondBr(b_.CreateICmpSLT(b_.CreateLoad(b_.getInt64Ty(), n_alloc), + b_.getInt64(0)), is_less_than_zero, is_not_less_than_zero); b_.SetInsertPoint(is_less_than_zero); - createRet(b_.CreateLoad(result)); + createRet(b_.CreateLoad(b_.getInt64Ty(), result)); b_.SetInsertPoint(is_not_less_than_zero); // test for equal to zero BasicBlock *is_zero = BasicBlock::Create(module_->getContext(), "hist.is_zero", log2_func); BasicBlock *is_not_zero = BasicBlock::Create(module_->getContext(), "hist.is_not_zero", log2_func); - b_.CreateCondBr(b_.CreateICmpEQ(b_.CreateLoad(n_alloc), b_.getInt64(0)), + b_.CreateCondBr(b_.CreateICmpEQ(b_.CreateLoad(b_.getInt64Ty(), n_alloc), + b_.getInt64(0)), is_zero, is_not_zero); b_.SetInsertPoint(is_zero); b_.CreateStore(b_.getInt64(1), result); - createRet(b_.CreateLoad(result)); + createRet(b_.CreateLoad(b_.getInt64Ty(), result)); b_.SetInsertPoint(is_not_zero); // power-of-2 index, offset by +2 b_.CreateStore(b_.getInt64(2), result); for (int i = 4; i >= 0; i--) { - Value *n = b_.CreateLoad(n_alloc); + Value *n = b_.CreateLoad(b_.getInt64Ty(), n_alloc); Value *shift = b_.CreateShl(b_.CreateIntCast(b_.CreateICmpSGE(b_.CreateIntCast(n, b_.getInt64Ty(), false), b_.getInt64(1 << (1<getFunction("log2"); } @@ -2650,8 +2781,8 @@ // algorithm { - Value *min = b_.CreateLoad(min_alloc); - Value *val = b_.CreateLoad(value_alloc); + Value *min = b_.CreateLoad(b_.getInt64Ty(), min_alloc); + Value *val = b_.CreateLoad(b_.getInt64Ty(), value_alloc); cmp = b_.CreateICmpSLT(val, min); } BasicBlock *lt_min = BasicBlock::Create(module_->getContext(), "lhist.lt_min", linear_func); @@ -2663,8 +2794,8 @@ b_.SetInsertPoint(ge_min); { - Value *max = b_.CreateLoad(max_alloc); - Value *val = b_.CreateLoad(value_alloc); + Value *max = b_.CreateLoad(b_.getInt64Ty(), max_alloc); + Value *val = b_.CreateLoad(b_.getInt64Ty(), value_alloc); cmp = b_.CreateICmpSGT(val, max); } BasicBlock *le_max = BasicBlock::Create(module_->getContext(), "lhist.le_max", linear_func); @@ -2673,22 +2804,22 @@ b_.SetInsertPoint(gt_max); { - Value *step = b_.CreateLoad(step_alloc); - Value *min = b_.CreateLoad(min_alloc); - Value *max = b_.CreateLoad(max_alloc); + Value *step = b_.CreateLoad(b_.getInt64Ty(), step_alloc); + Value *min = b_.CreateLoad(b_.getInt64Ty(), min_alloc); + Value *max = b_.CreateLoad(b_.getInt64Ty(), max_alloc); Value *div = b_.CreateUDiv(b_.CreateSub(max, min), step); b_.CreateStore(b_.CreateAdd(div, b_.getInt64(1)), result_alloc); - createRet(b_.CreateLoad(result_alloc)); + createRet(b_.CreateLoad(b_.getInt64Ty(), result_alloc)); } b_.SetInsertPoint(le_max); { - Value *step = b_.CreateLoad(step_alloc); - Value *min = b_.CreateLoad(min_alloc); - Value *val = b_.CreateLoad(value_alloc); + Value *step = b_.CreateLoad(b_.getInt64Ty(), step_alloc); + Value *min = b_.CreateLoad(b_.getInt64Ty(), min_alloc); + Value *val = b_.CreateLoad(b_.getInt64Ty(), value_alloc); Value *div3 = b_.CreateUDiv(b_.CreateSub(val, min), step); b_.CreateStore(b_.CreateAdd(div3, b_.getInt64(1)), result_alloc); - createRet(b_.CreateLoad(result_alloc)); + createRet(b_.CreateLoad(b_.getInt64Ty(), result_alloc)); } b_.restoreIP(ip); @@ -2742,14 +2873,18 @@ // as the struct is not packed we need to memset it. b_.CREATE_MEMSET(fmt_args, b_.getInt8(0), struct_size, 1); - Value *id_offset = b_.CreateGEP(fmt_args, {b_.getInt32(0), b_.getInt32(0)}); + Value *id_offset = b_.CreateGEP(fmt_struct, + fmt_args, + { b_.getInt32(0), b_.getInt32(0) }); b_.CreateStore(b_.getInt64(id + asyncactionint(async_action)), id_offset); for (size_t i=1; isize(); i++) { Expression &arg = *call.vargs->at(i); auto scoped_del = accept(&arg); - Value *offset = b_.CreateGEP(fmt_args, {b_.getInt32(0), b_.getInt32(i)}); + Value *offset = b_.CreateGEP(fmt_struct, + fmt_args, + { b_.getInt32(0), b_.getInt32(i) }); if (needMemcpy(arg.type)) b_.CREATE_MEMCPY(offset, expr_, arg.type.GetSize(), 1); else if (arg.type.IsIntegerTy() && arg.type.GetSize() < 8) @@ -2789,10 +2924,12 @@ // Pull out function argument Value *ctx = func->arg_begin(); int offset = arch::arg_offset(arg_num); - Value *addr = b_.CreateLoad( - b_.getInt64Ty(), - b_.CreateGEP(ctx, b_.getInt64(offset * sizeof(uintptr_t))), - "arg" + std::to_string(arg_num)); + Value *arg = b_.CreateGEP(b_.getInt8Ty(), + ctx, + b_.getInt64(offset * sizeof(uintptr_t))); + Value *addr = b_.CreateLoad(b_.getInt64Ty(), + arg, + "arg" + std::to_string(arg_num)); // Tell userspace to setup the real watchpoint auto elements = AsyncEvent::Watchpoint().asLLVMType(b_); @@ -2803,12 +2940,16 @@ size_t struct_size = datalayout().getTypeAllocSize(watchpoint_struct); // Fill in perf event struct - b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::watchpoint_attach)), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); - b_.CreateStore(b_.getInt64(watchpoint_id_), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); + b_.CreateStore( + b_.getInt64(asyncactionint(AsyncAction::watchpoint_attach)), + b_.CreateGEP(watchpoint_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); + b_.CreateStore( + b_.getInt64(watchpoint_id_), + b_.CreateGEP(watchpoint_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); watchpoint_id_++; - b_.CreateStore(addr, b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(2) })); + b_.CreateStore( + addr, + b_.CreateGEP(watchpoint_struct, buf, { b_.getInt64(0), b_.getInt32(2) })); b_.CreatePerfEventOutput(ctx, buf, struct_size); b_.CreateLifetimeEnd(buf); @@ -2827,11 +2968,14 @@ call.func + "_" + map.ident); // store asyncactionid: - b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::print)), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); + b_.CreateStore( + b_.getInt64(asyncactionint(AsyncAction::print)), + b_.CreateGEP(print_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); auto id = bpftrace_.maps[map.ident].value()->id; - auto *ident_ptr = b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) }); + auto *ident_ptr = b_.CreateGEP(print_struct, + buf, + { b_.getInt64(0), b_.getInt32(1) }); b_.CreateStore(b_.GetIntSameSize(id, elements.at(1)), ident_ptr); // top, div @@ -2843,14 +2987,16 @@ auto scoped_del = accept(call.vargs->at(arg_idx)); b_.CreateStore(b_.CreateIntCast(expr_, elements.at(arg_idx), false), - b_.CreateGEP(buf, + b_.CreateGEP(print_struct, + buf, { b_.getInt64(0), b_.getInt32(arg_idx + 1) })); } for (; arg_idx < 3; arg_idx++) { b_.CreateStore(b_.GetIntSameSize(0, elements.at(arg_idx)), - b_.CreateGEP(buf, + b_.CreateGEP(print_struct, + buf, { b_.getInt64(0), b_.getInt32(arg_idx + 1) })); } @@ -2875,15 +3021,19 @@ size_t struct_size = datalayout().getTypeAllocSize(print_struct); // Store asyncactionid: - b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::print_non_map)), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(0) })); + b_.CreateStore( + b_.getInt64(asyncactionint(AsyncAction::print_non_map)), + b_.CreateGEP(print_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); // Store print id - b_.CreateStore(b_.getInt64(id), - b_.CreateGEP(buf, { b_.getInt64(0), b_.getInt32(1) })); + b_.CreateStore( + b_.getInt64(id), + b_.CreateGEP(print_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); // Store content - Value *content_offset = b_.CreateGEP(buf, { b_.getInt32(0), b_.getInt32(2) }); + Value *content_offset = b_.CreateGEP(print_struct, + buf, + { b_.getInt32(0), b_.getInt32(2) }); b_.CREATE_MEMSET(content_offset, b_.getInt8(0), arg.type.GetSize(), 1); if (needMemcpy(arg.type)) { @@ -2962,6 +3112,35 @@ void CodegenLLVM::optimize() { assert(state_ == State::IR); + +#if LLVM_VERSION_MAJOR >= 14 + PipelineTuningOptions pto; + pto.LoopUnrolling = false; + pto.LoopInterleaving = false; + pto.LoopVectorization = false; + pto.SLPVectorization = false; + + llvm::PassBuilder pb(&orc_->getTargetMachine(), pto); + + // ModuleAnalysisManager must be destroyed first. + llvm::LoopAnalysisManager lam; + llvm::FunctionAnalysisManager fam; + llvm::CGSCCAnalysisManager cgam; + llvm::ModuleAnalysisManager mam; + + // Register all the basic analyses with the managers. + pb.registerModuleAnalyses(mam); + pb.registerCGSCCAnalyses(cgam); + pb.registerFunctionAnalyses(fam); + pb.registerLoopAnalyses(lam); + pb.crossRegisterProxies(lam, fam, cgam, mam); + + ModulePassManager mpm = pb.buildPerModuleDefaultPipeline( + llvm::OptimizationLevel::O3, + /*LTOPreLink=*/false); + mpm.run(*module_, mam); + +#else PassManagerBuilder PMB; PMB.OptLevel = 3; legacy::PassManager PM; @@ -2977,6 +3156,8 @@ PMB.populateModulePassManager(PM); PM.run(*module_.get()); +#endif + state_ = State::OPT; } @@ -3025,6 +3206,15 @@ module_->print(os, nullptr, false, true); } +void CodegenLLVM::DumpIR(const std::string filename) +{ + assert(module_.get() != nullptr); + std::ofstream file; + file.open(filename); + raw_os_ostream os(file); + module_->print(os, nullptr, false, true); +} + CodegenLLVM::ScopedExprDeleter CodegenLLVM::accept(Node *node) { expr_deleter_ = nullptr; @@ -3054,7 +3244,9 @@ src_data = b_.CreateIntToPtr(src_data, b_.GetType(data_type)->getPointerTo()); - Value *src = b_.CreateGEP(src_data, { b_.getInt32(0), index }); + Value *src = b_.CreateGEP(b_.GetType(data_type), + src_data, + { b_.getInt32(0), index }); // It may happen that the result pointer type is not correct, in such case // do a typecast @@ -3065,7 +3257,7 @@ if (elem_type.IsIntegerTy() || elem_type.IsPtrTy()) { // Load the correct type from src - expr_ = b_.CreateLoad(src, true); + expr_ = b_.CreateLoad(b_.GetType(elem_type), src, true); } else { @@ -3119,7 +3311,8 @@ // Read data onto stack if (data_type.IsCtxAccess()) { - expr_ = b_.CreateLoad(b_.CreateIntToPtr(src, dst_type->getPointerTo()), + expr_ = b_.CreateLoad(dst_type, + b_.CreateIntToPtr(src, dst_type->getPointerTo()), true); expr_ = b_.CreateIntCast(expr_, b_.getInt64Ty(), elem_type.IsSigned()); @@ -3150,9 +3343,7 @@ AllocaInst *dst = b_.CreateAllocaBPF(elem_type, temp_name); b_.CreateProbeRead( ctx_, dst, elem_type.GetSize(), src, data_type.GetAS(), loc); - expr_ = b_.CreateIntCast(b_.CreateLoad(dst), - b_.getInt64Ty(), - elem_type.IsSigned()); + expr_ = b_.CreateLoad(b_.GetType(elem_type), dst); b_.CreateLifetimeEnd(dst); } } @@ -3181,13 +3372,15 @@ if (unop.is_post_op) expr_ = oldval; else - expr_ = b_.CreateLoad(newval); + expr_ = b_.CreateLoad(b_.GetType(map.type), newval); b_.CreateLifetimeEnd(newval); } else if (unop.expr->is_variable) { Variable &var = static_cast(*unop.expr); - Value *oldval = b_.CreateLoad(variables_[var.ident]); + Value *oldval = b_.CreateLoad( + variables_[var.ident]->getType()->getPointerElementType(), + variables_[var.ident]); Value *newval; if (is_increment) newval = b_.CreateAdd(oldval, b_.GetIntSameSize(step, oldval)); diff -Nru bpftrace-0.14.1/src/ast/passes/codegen_llvm.h bpftrace-0.15.0/src/ast/passes/codegen_llvm.h --- bpftrace-0.14.1/src/ast/passes/codegen_llvm.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/codegen_llvm.h 2022-05-24 10:41:38.000000000 +0000 @@ -6,24 +6,25 @@ #include #include -#include "ast/bpforc/bpforc.h" -#include "bpftrace.h" -#include "irbuilderbpf.h" -#include "location.hh" -#include "map.h" -#include "visitors.h" - #include #include #include #include +#include "ast/bpforc/bpforc.h" +#include "ast/irbuilderbpf.h" +#include "ast/visitors.h" +#include "bpftrace.h" +#include "format_string.h" +#include "location.hh" +#include "map.h" + namespace bpftrace { namespace ast { using namespace llvm; -using CallArgs = std::vector>>; +using CallArgs = std::vector>>; class CodegenLLVM : public Visitor { @@ -65,6 +66,7 @@ // Exists to make calling from a debugger easier void DumpIR(void); void DumpIR(std::ostream &out); + void DumpIR(const std::string filename); void createFormatStringCall(Call &call, int &id, CallArgs &call_args, const std::string &call_name, AsyncAction async_action); @@ -89,8 +91,17 @@ ScopedExprDeleter(const ScopedExprDeleter &other) = delete; ScopedExprDeleter &operator=(const ScopedExprDeleter &other) = delete; - ScopedExprDeleter(ScopedExprDeleter &&other) = default; - ScopedExprDeleter &operator=(ScopedExprDeleter &&other) = default; + + ScopedExprDeleter(ScopedExprDeleter &&other) + { + *this = std::move(other); + } + + ScopedExprDeleter &operator=(ScopedExprDeleter &&other) + { + deleter_ = other.disarm(); + return *this; + } ~ScopedExprDeleter() { @@ -207,6 +218,7 @@ AttachPoint *current_attach_point_ = nullptr; std::string probefull_; std::string tracepoint_struct_; + uint64_t probe_count_ = 0; std::map next_probe_index_; // Used if there are duplicate USDT entries int current_usdt_location_index_{ 0 }; @@ -221,6 +233,7 @@ int system_id_ = 0; int non_map_print_id_ = 0; uint64_t watchpoint_id_ = 0; + int cgroup_path_id_ = 0; Function *linear_func_ = nullptr; Function *log2_func_ = nullptr; diff -Nru bpftrace-0.14.1/src/ast/passes/field_analyser.cpp bpftrace-0.15.0/src/ast/passes/field_analyser.cpp --- bpftrace-0.14.1/src/ast/passes/field_analyser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/field_analyser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,9 +1,11 @@ #include "field_analyser.h" + +#include +#include + #include "dwarf_parser.h" #include "log.h" #include "probe_matcher.h" -#include -#include namespace bpftrace { namespace ast { @@ -91,14 +93,20 @@ auto it = var_types_.find(map.ident); if (it != var_types_.end()) - type_ = it->second; + { + type_ = it->second.first; + sized_type_ = it->second.second; + } } void FieldAnalyser::visit(Variable &var __attribute__((unused))) { auto it = var_types_.find(var.ident); if (it != var_types_.end()) - type_ = it->second; + { + type_ = it->second.first; + sized_type_ = it->second.second; + } } void FieldAnalyser::visit(FieldAccess &acc) @@ -119,6 +127,7 @@ type_ = it->second.GetPointeeTy()->GetName(); else type_ = ""; + sized_type_ = it->second; } bpftrace_.btf_set_.insert(type_); @@ -128,6 +137,15 @@ { type_ = bpftrace_.btf_.type_of(type_, acc.field); bpftrace_.btf_set_.insert(type_); + + if (sized_type_.IsPtrTy()) + { + sized_type_ = *sized_type_.GetPointeeTy(); + resolve_fields(sized_type_); + } + + if (sized_type_.IsRecordTy() && sized_type_.HasField(acc.field)) + sized_type_ = sized_type_.GetField(acc.field).type; } } @@ -137,24 +155,31 @@ type_ = cast.cast_type; assert(!type_.empty()); bpftrace_.btf_set_.insert(type_); + + for (auto &ap : *probe_->attach_points) + if (Dwarf *dwarf = bpftrace_.get_dwarf(*ap)) + sized_type_ = dwarf->get_stype(cast.cast_type); } void FieldAnalyser::visit(AssignMapStatement &assignment) { Visit(*assignment.map); Visit(*assignment.expr); - var_types_.emplace(assignment.map->ident, type_); + var_types_.emplace(assignment.map->ident, std::make_pair(type_, sized_type_)); } void FieldAnalyser::visit(AssignVarStatement &assignment) { Visit(*assignment.expr); - var_types_.emplace(assignment.var->ident, type_); + var_types_.emplace(assignment.var->ident, std::make_pair(type_, sized_type_)); } bool FieldAnalyser::compare_args(const ProbeArgs &args1, const ProbeArgs &args2) { - auto pred = [](auto a, auto b) { return a.first == b.first; }; + using ProbeArgsValue = ProbeArgs::value_type; + auto pred = [](const ProbeArgsValue &a, const ProbeArgsValue &b) { + return a.first == b.first; + }; return args1.size() == args2.size() && std::equal(args1.begin(), args1.end(), args2.begin(), pred); @@ -291,6 +316,16 @@ return true; } +void FieldAnalyser::resolve_fields(SizedType &type) +{ + if (!type.IsRecordTy()) + return; + + for (auto &ap : *probe_->attach_points) + if (Dwarf *dwarf = bpftrace_.get_dwarf(*ap)) + dwarf->resolve_fields(sized_type_); +} + void FieldAnalyser::visit(Probe &probe) { probe_ = &probe; diff -Nru bpftrace-0.14.1/src/ast/passes/field_analyser.h bpftrace-0.15.0/src/ast/passes/field_analyser.h --- bpftrace-0.14.1/src/ast/passes/field_analyser.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/field_analyser.h 2022-05-24 10:41:38.000000000 +0000 @@ -1,11 +1,12 @@ #pragma once -#include "bpftrace.h" -#include "visitors.h" #include #include #include +#include "ast/visitors.h" +#include "bpftrace.h" + namespace bpftrace { namespace ast { @@ -37,11 +38,13 @@ private: bool resolve_args(Probe &probe); bool compare_args(const ProbeArgs &args1, const ProbeArgs &args2); + void resolve_fields(SizedType &type); Node *root_; ProbeType probe_type_; std::string attach_func_; std::string type_; + SizedType sized_type_; BPFtrace &bpftrace_; bpf_prog_type prog_type_; bool has_builtin_args_; @@ -51,7 +54,7 @@ std::ostringstream err_; ProbeArgs ap_args_; - std::map var_types_; + std::map> var_types_; }; } // namespace ast diff -Nru bpftrace-0.14.1/src/ast/passes/node_counter.h bpftrace-0.15.0/src/ast/passes/node_counter.h --- bpftrace-0.14.1/src/ast/passes/node_counter.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/node_counter.h 2022-05-24 10:41:38.000000000 +0000 @@ -1,8 +1,9 @@ #pragma once + +#include "ast/pass_manager.h" +#include "ast/visitors.h" #include "bpftrace.h" #include "log.h" -#include "pass_manager.h" -#include "visitors.h" namespace bpftrace { namespace ast { @@ -25,7 +26,7 @@ size_t count_ = 0; }; -Pass CreateCounterPass() +inline Pass CreateCounterPass() { auto fn = [](Node &n, PassContext &ctx) { NodeCounter c; diff -Nru bpftrace-0.14.1/src/ast/passes/portability_analyser.h bpftrace-0.15.0/src/ast/passes/portability_analyser.h --- bpftrace-0.14.1/src/ast/passes/portability_analyser.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/portability_analyser.h 2022-05-24 10:41:38.000000000 +0000 @@ -3,8 +3,8 @@ #include #include -#include "pass_manager.h" -#include "visitors.h" +#include "ast/pass_manager.h" +#include "ast/visitors.h" namespace bpftrace { namespace ast { diff -Nru bpftrace-0.14.1/src/ast/passes/printer.cpp bpftrace-0.15.0/src/ast/passes/printer.cpp --- bpftrace-0.14.1/src/ast/passes/printer.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/printer.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,10 +1,11 @@ +#include "ast/passes/printer.h" + #include #include #include #include -#include "ast.h" -#include "printer.h" +#include "ast/ast.h" namespace bpftrace { namespace ast { diff -Nru bpftrace-0.14.1/src/ast/passes/printer.h bpftrace-0.15.0/src/ast/passes/printer.h --- bpftrace-0.14.1/src/ast/passes/printer.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/printer.h 2022-05-24 10:41:38.000000000 +0000 @@ -1,8 +1,9 @@ #pragma once -#include "visitors.h" #include +#include "ast/visitors.h" + namespace bpftrace { namespace ast { diff -Nru bpftrace-0.14.1/src/ast/passes/resource_analyser.cpp bpftrace-0.15.0/src/ast/passes/resource_analyser.cpp --- bpftrace-0.14.1/src/ast/passes/resource_analyser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/resource_analyser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -38,7 +38,7 @@ Visit(*root_); prepare_seq_printf_ids(); - return resources_; + return std::move(resources_); } void ResourceAnalyser::visit(Probe &probe) @@ -65,8 +65,8 @@ if (call.func == "printf" || call.func == "system" || call.func == "cat") { - std::string fmt = get_literal_string(*call.vargs->at(0)); std::vector args; + // NOTE: the same logic can be found in the semantic_analyser pass for (auto it = call.vargs->begin() + 1; it != call.vargs->end(); it++) { // Promote to 64-bit if it's not an aggregate type @@ -88,25 +88,26 @@ }); } + auto fmtstr = get_literal_string(*call.vargs->at(0)); if (call.func == "printf") { if (single_provider_type_postsema(probe_) == ProbeType::iter) { - resources_.seq_printf_args.emplace_back(fmt, args); + resources_.seq_printf_args.emplace_back(fmtstr, args); resources_.needs_data_map = true; } else { - resources_.printf_args.emplace_back(fmt, args); + resources_.printf_args.emplace_back(fmtstr, args); } } else if (call.func == "system") { - resources_.system_args.emplace_back(fmt, args); + resources_.system_args.emplace_back(fmtstr, args); } else { - resources_.cat_args.emplace_back(fmt, args); + resources_.cat_args.emplace_back(fmtstr, args); } } else if (call.func == "join") @@ -152,6 +153,14 @@ { resources_.stackid_maps.insert(call.type.stack_type); } + else if (call.func == "cgroup_path") + { + if (call.vargs->size() > 1) + resources_.cgroup_path_args.push_back( + get_literal_string(*call.vargs->at(1))); + else + resources_.cgroup_path_args.push_back("*"); + } } void ResourceAnalyser::visit(Map &map) @@ -179,8 +188,7 @@ { auto fn = [](Node &n, PassContext &ctx) { ResourceAnalyser analyser{ &n }; - auto resources = analyser.analyse(); - ctx.b.resources = resources; + ctx.b.resources = analyser.analyse(); // Create fake maps so that codegen has access to map IDs // diff -Nru bpftrace-0.14.1/src/ast/passes/resource_analyser.h bpftrace-0.15.0/src/ast/passes/resource_analyser.h --- bpftrace-0.14.1/src/ast/passes/resource_analyser.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/resource_analyser.h 2022-05-24 10:41:38.000000000 +0000 @@ -3,9 +3,9 @@ #include #include -#include "pass_manager.h" +#include "ast/pass_manager.h" +#include "ast/visitors.h" #include "required_resources.h" -#include "visitors.h" namespace bpftrace { namespace ast { diff -Nru bpftrace-0.14.1/src/ast/passes/semantic_analyser.cpp bpftrace-0.15.0/src/ast/passes/semantic_analyser.cpp --- bpftrace-0.14.1/src/ast/passes/semantic_analyser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/semantic_analyser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,13 +1,5 @@ #include "semantic_analyser.h" -#include "arch/arch.h" -#include "ast.h" -#include "fake_map.h" -#include "log.h" -#include "printf.h" -#include "probe_matcher.h" -#include "signal_bt.h" -#include "tracepoint_format_parser.h" -#include "usdt.h" + #include #include #include @@ -16,6 +8,16 @@ #include +#include "arch/arch.h" +#include "ast/ast.h" +#include "ast/signal_bt.h" +#include "fake_map.h" +#include "log.h" +#include "printf.h" +#include "probe_matcher.h" +#include "tracepoint_format_parser.h" +#include "usdt.h" + namespace bpftrace { namespace ast { @@ -80,21 +82,18 @@ void SemanticAnalyser::visit(String &string) { + string.type = CreateString(string.str.size() + 1); // Skip check for printf()'s format string (1st argument) and create the // string with the original size. This is because format string is not part of // bpf byte code. if (func_ == "printf" && func_arg_idx_ == 0) - { - string.type = CreateString(string.str.size()); return; - } if (!is_compile_time_func(func_) && string.str.size() > STRING_SIZE - 1) { LOG(ERROR, string.loc, err_) << "String is too long (over " << STRING_SIZE << " bytes): " << string.str; } - string.type = CreateString(STRING_SIZE); // @a = buf("hi", 2). String allocated on bpf stack. See codegen string.type.SetAS(AddrSpace::kernel); } @@ -925,6 +924,7 @@ check_arg(call, Type::string, 0, true); if (is_final_pass()) { + // NOTE: the same logic can be found in the resource_analyser pass auto &fmt_arg = *call.vargs->at(0); String &fmt = static_cast(fmt_arg); std::vector args; @@ -950,7 +950,7 @@ }, }); } - std::string msg = verify_format_string(fmt.str, args); + std::string msg = validate_format_string(fmt.str, args); if (msg != "") { LOG(ERROR, call.loc, err_) << msg; @@ -1022,6 +1022,15 @@ } } } + else if (call.func == "cgroup_path") + { + call.type = CreateCgroupPath(); + if (check_varargs(call, 1, 2)) + { + check_arg(call, Type::integer, 0, false); + call.vargs->size() > 1 && check_arg(call, Type::string, 1, false); + } + } else if (call.func == "clear") { check_assignment(call, false, false, false); if (check_nargs(call, 1)) { @@ -1238,6 +1247,10 @@ LOG(ERROR, call.loc, err_) << call.func << "() argument must be 6 bytes in size"; + if (type.IsStringTy() && arg->is_literal) + LOG(ERROR, call.loc, err_) + << call.func << "() does not support literal string arguments"; + call.type = CreateMacAddress(); } else if (call.func == "unwatch") @@ -1248,6 +1261,22 @@ // Return type cannot be used call.type = SizedType(Type::none, 0); } + else if (call.func == "bswap") + { + if (!check_nargs(call, 1)) + return; + + Expression *arg = call.vargs->at(0); + if (arg->type.type != Type::integer) + { + LOG(ERROR, call.loc, err_) + << call.func << "() only supports integer arguments (" + << arg->type.type << " provided)"; + return; + } + + call.type = CreateUInt(arg->type.GetIntBitWidth()); + } else { LOG(ERROR, call.loc, err_) << "Unknown function: '" << call.func << "'"; @@ -1360,38 +1389,21 @@ " array instead (eg `@map[$1, $2] = ...)`."; } - if (is_final_pass()) { - if (expr->type.IsNoneTy()) - LOG(ERROR, expr->loc, err_) << "Invalid expression for assignment: "; + if (is_final_pass() && expr->type.IsNoneTy()) + LOG(ERROR, expr->loc, err_) << "Invalid expression for assignment: "; - SizedType keytype = expr->type; - // Skip.IsSigned() when comparing keys to not break existing scripts - // which use maps as a lookup table - // TODO (fbs): This needs a better solution - if (expr->type.IsIntTy()) - keytype = CreateUInt(keytype.GetSize() * 8); - key.args_.push_back(keytype); - } + SizedType keytype = expr->type; + // Skip.IsSigned() when comparing keys to not break existing scripts + // which use maps as a lookup table + // TODO (fbs): This needs a better solution + if (expr->type.IsIntTy()) + keytype = CreateUInt(keytype.GetSize() * 8); + key.args_.push_back(keytype); } } - if (is_final_pass()) { - if (!map.skip_key_validation) { - auto search = map_key_.find(map.ident); - if (search != map_key_.end()) { - if (search->second != key) { - LOG(ERROR, map.loc, err_) - << "Argument mismatch for " << map.ident << ": " - << "trying to access with arguments: " << key.argument_type_list() - << " when map expects arguments: " - << search->second.argument_type_list(); - } - } - else { - map_key_.insert({map.ident, key}); - } - } - } + if (pass_ > 1 && !map.skip_key_validation) + update_key_type(map, key); auto search_val = map_val_.find(map.ident); if (search_val != map_val_.end()) { @@ -1413,8 +1425,9 @@ void SemanticAnalyser::visit(Variable &var) { - auto search_val = variable_val_.find(var.ident); - if (search_val != variable_val_.end()) { + auto search_val = variable_val_[probe_].find(var.ident); + if (search_val != variable_val_[probe_].end()) + { var.type = search_val->second; } else { @@ -2247,20 +2260,11 @@ { auto map_size = map_val_[map_ident].GetSize(); auto expr_size = assignment.expr->type.GetSize(); - if (map_size != expr_size) + if (map_size < expr_size) { - std::stringstream buf; - buf << "String size mismatch: " << map_size << " != " << expr_size << "."; - if (map_size < expr_size) - { - buf << " The value may be truncated."; - LOG(WARNING, assignment.loc, out_) << buf.str(); - } - else - { - // bpf_map_update_elem() expects map_size-length value - LOG(ERROR, assignment.loc, err_) << buf.str(); - } + LOG(WARNING, assignment.loc, out_) + << "String size mismatch: " << map_size << " < " << expr_size + << ". The value may be truncated."; } } else if (type.IsBufferTy()) @@ -2296,7 +2300,7 @@ { const auto &map_type = map_val_[map_ident]; const auto &expr_type = assignment.expr->type; - if (map_type != expr_type) + if (!expr_type.FitsInto(map_type)) { LOG(ERROR, assignment.loc, err_) << "Tuple type mismatch: " << map_type << " != " << expr_type << "."; @@ -2311,6 +2315,11 @@ { map_val_[map_ident].is_internal = true; } + else + { + LOG(ERROR, assignment.loc, err_) + << "Array type mismatch: " << map_type << " != " << expr_type << "."; + } } if (is_final_pass()) @@ -2326,8 +2335,9 @@ assignment.expr->accept(*this); std::string var_ident = assignment.var->ident; - auto search = variable_val_.find(var_ident); - assignment.var->type = assignment.expr->type; + auto search = variable_val_[probe_].find(var_ident); + + auto &assignTy = assignment.expr->type; auto *builtin = dynamic_cast(assignment.expr); if (builtin && builtin->ident == "args" && builtin->type.is_funcarg) @@ -2335,33 +2345,39 @@ LOG(ERROR, assignment.loc, err_) << "args cannot be assigned to a variable"; } - if (search != variable_val_.end()) { + if (search != variable_val_[probe_].end()) + { if (search->second.IsNoneTy()) { if (is_final_pass()) { LOG(ERROR, assignment.loc, err_) << "Undefined variable: " + var_ident; } else { - search->second = assignment.expr->type; + search->second = assignTy; } } - else if (!search->second.IsSameType(assignment.expr->type)) + else if ((search->second.IsStringTy() && assignTy.IsStringTy()) || + (search->second.IsTupleTy() && assignTy.IsTupleTy())) + { + update_string_size(search->second, assignTy); + } + else if (!search->second.IsSameType(assignTy)) { LOG(ERROR, assignment.loc, err_) << "Type mismatch for " << var_ident << ": " - << "trying to assign value of type '" << assignment.expr->type + << "trying to assign value of type '" << assignTy << "' when variable already contains a value of type '" << search->second << "'"; } } else { // This variable hasn't been seen before - variable_val_[var_ident] = assignment.expr->type; - assignment.var->type = assignment.expr->type; + variable_val_[probe_].insert({ var_ident, assignment.expr->type }); } - auto &storedTy = variable_val_[var_ident]; - auto &assignTy = assignment.expr->type; + auto &storedTy = variable_val_[probe_][var_ident]; + + assignment.var->type = storedTy; if (assignTy.IsRecordTy()) { @@ -2377,12 +2393,11 @@ { auto var_size = storedTy.GetSize(); auto expr_size = assignTy.GetSize(); - if (var_size != expr_size) + if (var_size < expr_size) { LOG(WARNING, assignment.loc, out_) << "String size mismatch: " << var_size << " != " << expr_size - << (var_size < expr_size ? ". The value may be truncated." - : ". The value may contain garbage."); + << ". The value may be truncated."; } } else if (assignTy.IsBufferTy()) @@ -2405,7 +2420,7 @@ { auto var_type = storedTy; auto expr_type = assignTy; - if (var_type != expr_type) + if (!expr_type.FitsInto(var_type)) { LOG(ERROR, assignment.loc, err_) << "Tuple type mismatch: " << var_type << " != " << expr_type << "."; @@ -2765,8 +2780,6 @@ { auto aps = probe.attach_points->size(); - // Clear out map of variable names - variables should be probe-local - variable_val_.clear(); probe_ = &probe; for (AttachPoint *ap : *probe.attach_points) { @@ -3113,41 +3126,6 @@ return &search->second; } -void SemanticAnalyser::update_assign_map_type(const Map &map, - SizedType &type, - const SizedType &new_type) -{ - const std::string &map_ident = map.ident; - if ((type.IsTupleTy() && new_type.IsTupleTy() && - type.GetFields().size() != new_type.GetFields().size()) || - (type.type != new_type.type) || - (type.IsRecordTy() && type.GetName() != new_type.GetName()) || - (type.IsArrayTy() && type != new_type)) - { - LOG(ERROR, map.loc, err_) - << "Type mismatch for " << map_ident << ": " - << "trying to assign value of type '" << new_type - << "' when map already contains a value of type '" << type; - return; - } - - // all integers are 64bit - if (type.IsIntTy()) - return; - - if (type.IsTupleTy() && new_type.IsTupleTy()) - { - auto &fields = type.GetFields(); - auto &new_fields = new_type.GetFields(); - for (size_t i = 0; i < fields.size(); i++) - { - update_assign_map_type(map, fields[i].type, new_fields[i].type); - } - } - - type = new_type; -} - /* * assign_map_type * @@ -3175,7 +3153,9 @@ << "trying to assign value of type '" << type << "' when map already contains a value of type '" << *maptype; } - update_assign_map_type(map, *maptype, type); + + if (maptype->IsStringTy() || maptype->IsTupleTy()) + update_string_size(*maptype, type); } else { @@ -3208,6 +3188,74 @@ } } } +void SemanticAnalyser::update_key_type(const Map &map, const MapKey &new_key) +{ + auto key = map_key_.find(map.ident); + if (key != map_key_.end()) + { + bool valid = true; + if (key->second.args_.size() == new_key.args_.size()) + { + for (size_t i = 0; i < key->second.args_.size(); i++) + { + SizedType &key_type = key->second.args_[i]; + const SizedType &new_key_type = new_key.args_[i]; + if (key_type.IsStringTy() && new_key_type.IsStringTy()) + { + key_type.SetSize( + std::max(key_type.GetSize(), new_key_type.GetSize())); + } + else if (key_type != new_key_type) + { + valid = false; + break; + } + } + } + else + valid = false; + + if (!valid) + { + LOG(ERROR, map.loc, err_) + << "Argument mismatch for " << map.ident << ": " + << "trying to access with arguments: " << new_key.argument_type_list() + << " when map expects arguments: " + << key->second.argument_type_list(); + } + } + else + map_key_.insert({ map.ident, new_key }); +} + +bool SemanticAnalyser::update_string_size(SizedType &type, + const SizedType &new_type) +{ + if (type.IsStringTy() && new_type.IsStringTy() && + type.GetSize() != new_type.GetSize()) + { + type.SetSize(std::max(type.GetSize(), new_type.GetSize())); + return true; + } + + if (type.IsTupleTy() && new_type.IsTupleTy() && + type.GetFieldCount() == new_type.GetFieldCount()) + { + bool updated = false; + std::vector new_elems; + for (ssize_t i = 0; i < type.GetFieldCount(); i++) + { + if (update_string_size(type.GetField(i).type, new_type.GetField(i).type)) + updated = true; + new_elems.push_back(type.GetField(i).type); + } + if (updated) + type = CreateTuple(bpftrace_.structs.AddTuple(new_elems)); + return updated; + } + + return false; +} Pass CreateSemanticPass() { diff -Nru bpftrace-0.14.1/src/ast/passes/semantic_analyser.h bpftrace-0.15.0/src/ast/passes/semantic_analyser.h --- bpftrace-0.14.1/src/ast/passes/semantic_analyser.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/passes/semantic_analyser.h 2022-05-24 10:41:38.000000000 +0000 @@ -4,12 +4,12 @@ #include #include +#include "ast/pass_manager.h" +#include "ast/visitors.h" #include "bpffeature.h" #include "bpftrace.h" #include "map.h" -#include "pass_manager.h" #include "types.h" -#include "visitors.h" namespace bpftrace { namespace ast { @@ -99,9 +99,8 @@ SizedType *get_map_type(const Map &map); void assign_map_type(const Map &map, const SizedType &type); - void update_assign_map_type(const Map &map, - SizedType &type, - const SizedType &new_type); + void update_key_type(const Map &map, const MapKey &new_key); + bool update_string_size(SizedType &type, const SizedType &new_type); void builtin_args_tracepoint(AttachPoint *attach_point, Builtin &builtin); ProbeType single_provider_type(void); @@ -124,7 +123,7 @@ // SemanticAnalyser. int func_arg_idx_ = -1; - std::map variable_val_; + std::map> variable_val_; std::map map_val_; std::map map_key_; ProbeArgs ap_args_; diff -Nru bpftrace-0.14.1/src/ast/pass_manager.cpp bpftrace-0.15.0/src/ast/pass_manager.cpp --- bpftrace-0.14.1/src/ast/pass_manager.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/pass_manager.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,8 +1,9 @@ +#include "ast/pass_manager.h" + #include -#include +#include "ast/passes/printer.h" #include "bpftrace.h" -#include "passes/printer.h" namespace bpftrace { namespace ast { diff -Nru bpftrace-0.14.1/src/ast/signal.cpp bpftrace-0.15.0/src/ast/signal.cpp --- bpftrace-0.14.1/src/ast/signal.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/signal.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,4 +1,4 @@ -#include "signal_bt.h" +#include "ast/signal_bt.h" #include #include diff -Nru bpftrace-0.14.1/src/ast/visitors.cpp bpftrace-0.15.0/src/ast/visitors.cpp --- bpftrace-0.14.1/src/ast/visitors.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/visitors.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,5 +1,6 @@ -#include "visitors.h" -#include "ast.h" +#include "ast/visitors.h" + +#include "ast/ast.h" namespace bpftrace { namespace ast { diff -Nru bpftrace-0.14.1/src/ast/visitors.h bpftrace-0.15.0/src/ast/visitors.h --- bpftrace-0.14.1/src/ast/visitors.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/ast/visitors.h 2022-05-24 10:41:38.000000000 +0000 @@ -1,9 +1,9 @@ #pragma once -#include "vtable.h" #include -#include "ast.h" +#include "ast/ast.h" +#include "ast/vtable.h" namespace bpftrace { namespace ast { diff -Nru bpftrace-0.14.1/src/attached_probe.cpp bpftrace-0.15.0/src/attached_probe.cpp --- bpftrace-0.14.1/src/attached_probe.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/attached_probe.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -193,7 +193,7 @@ { load_prog(); if (bt_verbose) - std::cerr << "Attaching " << probe_.name << std::endl; + std::cerr << "Attaching " << probe_.orig_name << std::endl; switch (probe_.type) { case ProbeType::kprobe: @@ -305,7 +305,7 @@ if (err) LOG(ERROR) << "failed to detach probe: " << probe_.name; - if (progfd_ >= 0) + if (close_progfd_ && progfd_ >= 0) close(progfd_); } @@ -627,8 +627,48 @@ path, symbol, sym_offset, func_offset, safe_mode, probe_.type); } +std::map AttachedProbe::cached_prog_fds_; + +bool AttachedProbe::use_cached_progfd(void) +{ + // Enabled for so far only for kprobes/kretprobes + if (probe_.type != ProbeType::kprobe && probe_.type != ProbeType::kretprobe) + return false; + + // Only for a wildcard probe which does not need expansion, + // because we can have multiple programs attached to a single probe + if (!has_wildcard(probe_.orig_name) || probe_.need_expansion) + return false; + + // Keep map of loaded programs based on their 'orig_name', + // and make sure they are loaded just once and use cached + // fd as probe's progfd_. + // This way we prevent multiple copies of the same program + // loaded for wildcard probe. + auto search = cached_prog_fds_.find(probe_.orig_name); + + if (search != cached_prog_fds_.end()) + { + progfd_ = search->second; + close_progfd_ = false; + return true; + } + + return false; +} + +void AttachedProbe::cache_progfd(void) +{ + if (probe_.type != ProbeType::kprobe && probe_.type != ProbeType::kretprobe) + return; + cached_prog_fds_[probe_.orig_name] = progfd_; +} + void AttachedProbe::load_prog() { + if (use_cached_progfd()) + return; + uint8_t *insns = std::get<0>(func_); int prog_len = std::get<1>(func_); const char *license = "GPL"; @@ -636,9 +676,8 @@ uint64_t log_buf_size = probe_.log_size; auto log_buf = std::make_unique(log_buf_size); - char name[STRING_SIZE]; - const char *namep; - std::string tracing_type, tracing_name; + std::string name; + std::string tracing_type; { // Redirect stderr, so we don't get error messages from BCC @@ -652,11 +691,20 @@ if (bt_verbose) log_level = 1; - // bpf_prog_load rejects colons in the probe name - strncpy(name, probe_.name.c_str(), STRING_SIZE - 1); - namep = name; - if (strrchr(name, ':') != NULL) - namep = strrchr(name, ':') + 1; + if (probe_.type == ProbeType::kprobe || probe_.type == ProbeType::kretprobe) + { + // Use orig_name for program name so we get proper name for + // wildcard probes, replace wildcards with '.' + name = probe_.orig_name; + std::replace(name.begin(), name.end(), '*', '.'); + } + else + name = probe_.name; + + // bpf_prog_load rejects colons in the probe name, + // so start the name after the probe type, after ':' + if (auto last_colon = name.rfind(':'); last_colon != std::string::npos) + name = name.substr(last_colon + 1); // The bcc_prog_load function now recognizes 'kfunc__/kretfunc__' // prefixes and detects and fills in all the necessary BTF related @@ -668,8 +716,7 @@ if (tracing_type == "iter") tracing_type = "bpf_iter"; - tracing_name = tracing_type + "__" + namep; - namep = tracing_name.c_str(); + name = tracing_type + "__" + name; } for (int attempt = 0; attempt < 3; attempt++) @@ -688,7 +735,7 @@ struct bpf_load_program_attr attr = {}; attr.prog_type = progtype(probe_.type); - attr.name = namep; + attr.name = name.c_str(); attr.insns = reinterpret_cast(insns); attr.license = license; @@ -707,10 +754,10 @@ #else // HAVE_BCC_PROG_LOAD_XATTR #ifdef HAVE_BCC_PROG_LOAD progfd_ = bcc_prog_load(progtype(probe_.type), - namep, + name.c_str(), #else progfd_ = bpf_prog_load(progtype(probe_.type), - namep, + name.c_str(), #endif reinterpret_cast(insns), prog_len, @@ -757,6 +804,8 @@ << "The verifier log: " << std::endl << log_buf.get() << std::endl; } + + cache_progfd(); } void AttachedProbe::attach_kprobe(bool safe_mode) @@ -1018,8 +1067,11 @@ int perf_event_fd = bpf_attach_tracepoint(progfd_, probe_.path.c_str(), eventname().c_str()); - if (perf_event_fd < 0) + if (perf_event_fd < 0 && probe_.name == probe_.orig_name) + { + // do not fail if there are other attach points where attaching may succeed throw std::runtime_error("Error attaching probe: " + probe_.name); + } perf_event_fds_.push_back(perf_event_fd); } diff -Nru bpftrace-0.14.1/src/attached_probe.h bpftrace-0.15.0/src/attached_probe.h --- bpftrace-0.14.1/src/attached_probe.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/attached_probe.h 2022-05-24 10:41:38.000000000 +0000 @@ -68,9 +68,14 @@ void attach_iter(void); int detach_iter(void); + static std::map cached_prog_fds_; + bool use_cached_progfd(void); + void cache_progfd(void); + Probe &probe_; std::tuple func_; std::vector perf_event_fds_; + bool close_progfd_ = true; int progfd_ = -1; uint64_t offset_ = 0; int tracing_fd_ = -1; diff -Nru bpftrace-0.14.1/src/bpftrace.cpp bpftrace-0.15.0/src/bpftrace.cpp --- bpftrace-0.14.1/src/bpftrace.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/bpftrace.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -53,67 +53,7 @@ bool bt_quiet = false; bool bt_verbose = false; volatile sig_atomic_t BPFtrace::exitsig_recv = false; -const int FMT_BUF_SZ = 512; - -std::string format(std::string fmt, - std::vector> &args) -{ - std::string retstr; - auto buffer = std::vector(FMT_BUF_SZ); - auto check_snprintf_ret = [](int r) { - if (r < 0) - { - LOG(FATAL) << "format() error occurred: " << std::strerror(errno); - } - }; - // Args have been made safe for printing by now, so replace nonstandard format - // specifiers with %s - size_t start_pos = 0; - while ((start_pos = fmt.find("%r", start_pos)) != std::string::npos) - { - fmt.replace(start_pos, 2, "%s"); - start_pos += 2; - } - - auto tokens_begin = std::sregex_iterator(fmt.begin(), - fmt.end(), - format_specifier_re); - auto tokens_end = std::sregex_iterator(); - - // replace format string tokens with args one by one - int literal_text_pos = 0; // starting pos of literal text (text that is not - // format specifier) - int i = 0; // args index - while (tokens_begin != tokens_end) - { - // take out the literal text - retstr += fmt.substr(literal_text_pos, - tokens_begin->position() - literal_text_pos); - // replace current specifier with an arg - int r = args.at(i)->print(buffer.data(), - buffer.capacity(), - tokens_begin->str().c_str()); - - check_snprintf_ret(r); - if (static_cast(r) >= buffer.capacity()) - { - // the buffer is not big enough to hold the string, resize it - buffer.resize(r + 1); - int r = args.at(i)->print(buffer.data(), - buffer.capacity(), - tokens_begin->str().c_str()); - check_snprintf_ret(r); - } - retstr += std::string(buffer.data()); - // move to the next literal text - literal_text_pos = tokens_begin->position() + tokens_begin->length(); - ++tokens_begin; - ++i; - } - // append whatever is left - retstr += fmt.substr(literal_text_pos); - return retstr; -} +volatile sig_atomic_t BPFtrace::sigusr1_recv = false; BPFtrace::~BPFtrace() { @@ -295,6 +235,7 @@ probe.orig_name = p.name(); probe.ns = attach_point->ns; probe.name = attach_point->name(target, func_id); + probe.need_expansion = p.need_expansion; probe.freq = attach_point->freq; probe.address = attach_point->address; probe.func_offset = attach_point->func_offset; @@ -587,35 +528,37 @@ } auto id = printf_id - asyncactionint(AsyncAction::syscall); - auto fmt = std::get<0>(bpftrace->resources.system_args[id]); - auto args = std::get<1>(bpftrace->resources.system_args[id]); + auto &fmt = std::get<0>(bpftrace->resources.system_args[id]); + auto &args = std::get<1>(bpftrace->resources.system_args[id]); auto arg_values = bpftrace->get_arg_values(args, arg_data); bpftrace->out_->message(MessageType::syscall, - exec_system(format(fmt, arg_values).c_str()), + exec_system(fmt.format_str(arg_values).c_str()), false); return; } else if ( printf_id >= asyncactionint(AsyncAction::cat)) { auto id = printf_id - asyncactionint(AsyncAction::cat); - auto fmt = std::get<0>(bpftrace->resources.cat_args[id]); - auto args = std::get<1>(bpftrace->resources.cat_args[id]); + auto &fmt = std::get<0>(bpftrace->resources.cat_args[id]); + auto &args = std::get<1>(bpftrace->resources.cat_args[id]); auto arg_values = bpftrace->get_arg_values(args, arg_data); std::stringstream buf; - cat_file(format(fmt, arg_values).c_str(), bpftrace->cat_bytes_max_, buf); + cat_file(fmt.format_str(arg_values).c_str(), bpftrace->cat_bytes_max_, buf); bpftrace->out_->message(MessageType::cat, buf.str(), false); return; } // printf - auto fmt = std::get<0>(bpftrace->resources.printf_args[printf_id]); - auto args = std::get<1>(bpftrace->resources.printf_args[printf_id]); + auto &fmt = std::get<0>(bpftrace->resources.printf_args[printf_id]); + auto &args = std::get<1>(bpftrace->resources.printf_args[printf_id]); auto arg_values = bpftrace->get_arg_values(args, arg_data); - bpftrace->out_->message(MessageType::printf, format(fmt, arg_values), false); + bpftrace->out_->message(MessageType::printf, + fmt.format_str(arg_values), + false); } std::vector> BPFtrace::get_arg_values(const std::vector &args, uint8_t* arg_data) @@ -690,10 +633,10 @@ break; } case Type::buffer: - arg_values.push_back(std::make_unique(resolve_buf( + arg_values.push_back(std::make_unique( reinterpret_cast(arg_data + arg.offset)->content, reinterpret_cast(arg_data + arg.offset) - ->length))); + ->length)); break; case Type::ksym: arg_values.push_back( @@ -759,6 +702,15 @@ std::make_unique(resolve_mac_address( reinterpret_cast(arg_data + arg.offset)))); break; + case Type::cgroup_path: + arg_values.push_back(std::make_unique( + resolve_cgroup_path(reinterpret_cast( + arg_data + arg.offset) + ->cgroup_path_id, + reinterpret_cast( + arg_data + arg.offset) + ->cgroup_id))); + break; // fall through default: LOG(FATAL) << "invalid argument type"; @@ -1130,9 +1082,9 @@ bytecode_ = std::move(bytecode); - int epollfd = setup_perf_events(); - if (epollfd < 0) - return epollfd; + err = setup_perf_events(); + if (err) + return err; if (maps.Has(MapManager::Type::Elapsed)) { @@ -1231,7 +1183,7 @@ } else { - poll_perf_events(epollfd); + poll_perf_events(); } attached_probes_.clear(); @@ -1243,7 +1195,7 @@ if (run_special_probe("END_trigger", bytecode_, END_trigger)) return -1; - poll_perf_events(epollfd, true); + poll_perf_events(/* drain */ true); // Calls perf_reader_free() on all open perf buffers. open_perf_buffers_.clear(); @@ -1253,8 +1205,8 @@ int BPFtrace::setup_perf_events() { - int epollfd = epoll_create1(EPOLL_CLOEXEC); - if (epollfd == -1) + epollfd_ = epoll_create1(EPOLL_CLOEXEC); + if (epollfd_ == -1) { LOG(ERROR) << "Failed to create epollfd"; return -1; @@ -1283,21 +1235,29 @@ bpf_update_elem( maps[MapManager::Type::PerfEvent].value()->mapfd_, &cpu, &reader_fd, 0); - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, reader_fd, &ev) == -1) + if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, reader_fd, &ev) == -1) { LOG(ERROR) << "Failed to add perf reader to epoll"; return -1; } } - return epollfd; + return 0; } -void BPFtrace::poll_perf_events(int epollfd, bool drain) +void BPFtrace::poll_perf_events(bool drain) { + const int timeout_ms = 100; + + if (epollfd_ < 0) + { + LOG(ERROR) << "Invalid epollfd " << epollfd_; + return; + } + auto events = std::vector(online_cpus_); while (true) { - int ready = epoll_wait(epollfd, events.data(), online_cpus_, 100); + int ready = epoll_wait(epollfd_, events.data(), online_cpus_, timeout_ms); if (ready < 0 && errno == EINTR && !BPFtrace::exitsig_recv) { // We received an interrupt not caused by SIGINT, skip and run again continue; @@ -1323,6 +1283,13 @@ { return; } + + // Print all maps if we received a SIGUSR1 signal + if (BPFtrace::sigusr1_recv) + { + BPFtrace::sigusr1_recv = false; + print_maps(); + } } return; } @@ -1680,7 +1647,7 @@ { // We can ignore all error checking here b/c child.cpp:validate_cmd() has // already done it - auto args = split_string(cmd_, ' ', /* remove_empty= */ true); + auto args = split_string(cmd_, ' ', /* remove_empty */ true); assert(!args.empty()); return resolve_binary_path(args[0]).front(); } @@ -1949,6 +1916,21 @@ return std::string(addr); } +std::string BPFtrace::resolve_cgroup_path(uint64_t cgroup_path_id, + uint64_t cgroup_id) const +{ + auto paths = get_cgroup_paths(cgroup_id, + resources.cgroup_path_args[cgroup_path_id]); + std::stringstream result; + for (auto &pair : paths) + { + if (pair.second.empty()) + continue; + result << pair.first << ":" << pair.second << ","; + } + return result.str().substr(0, result.str().size() - 1); +} + #ifdef HAVE_BCC_ELF_FOREACH_SYM static int add_symbol(const char *symname, uint64_t /*start*/, uint64_t /*size*/, void *payload) { auto syms = static_cast *>(payload); @@ -2270,7 +2252,8 @@ auto dwarf = dwarves_.find(filename); if (dwarf == dwarves_.end()) { - dwarf = dwarves_.emplace(filename, Dwarf::GetFromBinary(filename)).first; + dwarf = + dwarves_.emplace(filename, Dwarf::GetFromBinary(this, filename)).first; } return dwarf->second.get(); } diff -Nru bpftrace-0.14.1/src/bpftrace.h bpftrace-0.15.0/src/bpftrace.h --- bpftrace-0.14.1/src/bpftrace.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/bpftrace.h 2022-05-24 10:41:38.000000000 +0000 @@ -126,6 +126,8 @@ struct symbol *sym, const std::string &path) const; std::string resolve_mac_address(const uint8_t *mac_addr) const; + std::string resolve_cgroup_path(uint64_t cgroup_path_id, + uint64_t cgroup_id) const; virtual std::string extract_func_symbols_from_path(const std::string &path) const; std::string resolve_probe(uint64_t probe_id) const; uint64_t resolve_cgroupid(const std::string &path) const; @@ -143,11 +145,11 @@ Dwarf *get_dwarf(const std::string &filename); Dwarf *get_dwarf(const ast::AttachPoint &attachpoint); - std::vector> attached_probes_; std::string cmd_; bool finalize_ = false; - // Global variable checking if an exit signal was received + // Global variables checking if an exit/usr1 signal was received static volatile sig_atomic_t exitsig_recv; + static volatile sig_atomic_t sigusr1_recv; RequiredResources resources; MapManager maps; @@ -156,6 +158,7 @@ std::map macros_; std::map enums_; std::unordered_set traceable_funcs_; + std::vector> attached_probes_; unsigned int join_argnum_ = 16; unsigned int join_argsize_ = 1024; @@ -166,6 +169,7 @@ uint64_t mapmax_ = 4096; size_t cat_bytes_max_ = 10240; uint64_t max_probes_ = 512; + uint64_t max_programs_ = 512; uint64_t log_size_ = 1000000; uint64_t perf_rb_pages_ = 64; uint64_t max_type_res_iterations = 0; @@ -214,12 +218,13 @@ int pid, bool file_activation); int setup_perf_events(); - void poll_perf_events(int epollfd, bool drain = false); + void poll_perf_events(bool drain = false); int print_map_hist(IMap &map, uint32_t top, uint32_t div); int print_map_stats(IMap &map, uint32_t top, uint32_t div); static uint64_t read_address_from_output(std::string output); std::vector find_empty_key(IMap &map, size_t size) const; bool has_iter_ = false; + int epollfd_ = -1; std::unordered_map> dwarves_; }; diff -Nru bpftrace-0.14.1/src/btf.cpp bpftrace-0.15.0/src/btf.cpp --- bpftrace-0.14.1/src/btf.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/btf.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -28,6 +28,15 @@ namespace bpftrace { +static __u32 type_cnt(const struct btf *btf) +{ +#ifdef HAVE_LIBBPF_BTF_TYPE_CNT + return btf__type_cnt(btf); +#else + return btf__get_nr_types(btf); +#endif +} + static unsigned char *get_data(const char *file, ssize_t *sizep) { struct stat st; @@ -185,6 +194,21 @@ free(str); } +static struct btf_dump *dump_new(const struct btf *btf, + btf_dump_printf_fn_t dump_printf, + void *ctx) +{ +#ifdef HAVE_LIBBPF_BTF_DUMP_NEW_V0_6_0 + return btf_dump__new(btf, dump_printf, ctx, nullptr); +#else + struct btf_dump_opts opts = { + .ctx = ctx, + }; + + return btf_dump__new(btf, nullptr, &opts, dump_printf); +#endif +} + static const char *btf_str(const struct btf *btf, __u32 off) { if (!off) @@ -220,12 +244,11 @@ return std::string(""); std::string ret = std::string(""); - struct btf_dump_opts opts = { .ctx = &ret, }; struct btf_dump *dump; char err_buf[256]; int err; - dump = btf_dump__new(btf, nullptr, &opts, dump_printf); + dump = dump_new(btf, dump_printf, &ret); err = libbpf_get_error(dump); if (err) { @@ -235,11 +258,14 @@ } std::unordered_set myset(set); - __s32 id, max = (__s32) btf__get_nr_types(btf); + __s32 id, max = (__s32)type_cnt(btf); for (id = 1; id <= max && myset.size(); id++) { const struct btf_type *t = btf__type_by_id(btf, id); + if (!t) + continue; + // Allow users to reference enum values by name to pull in entire enum defs if (btf_is_enum(t)) { @@ -283,6 +309,8 @@ return std::string(""); const struct btf_type *type = btf__type_by_id(btf, type_id); + if (!type) + return std::string(""); return type_of(type, field); } @@ -311,6 +339,8 @@ if (m_name == "") { const struct btf_type *type = btf__type_by_id(btf, m[i].type); + if (!type) + return std::string(""); std::string type_name = type_of(type, field); if (!type_name.empty()) return type_name; @@ -331,6 +361,8 @@ BTF_INFO_KIND(f->info) == BTF_KIND_RESTRICT) { f = btf__type_by_id(btf, f->type); + if (!f) + return std::string(""); } return full_type_str(btf, f); @@ -364,7 +396,7 @@ const struct btf_type *BTF::btf_type_skip_modifiers(const struct btf_type *t) { - while (btf_type_is_modifier(t)) + while (t && btf_type_is_modifier(t)) { t = btf__type_by_id(btf, t->type); } @@ -415,13 +447,16 @@ if (!has_data()) throw std::runtime_error("BTF data not available"); - __s32 id, max = (__s32)btf__get_nr_types(btf); + __s32 id, max = (__s32)type_cnt(btf); std::string name = func; for (id = 1; id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf, id); + if (!t) + continue; + if (!btf_is_func(t)) continue; @@ -431,7 +466,7 @@ continue; t = btf__type_by_id(btf, t->type); - if (!btf_is_func_proto(t)) + if (!t || !btf_is_func_proto(t)) { throw std::runtime_error("not a function"); } @@ -486,17 +521,14 @@ std::unique_ptr BTF::get_all_funcs() const { - __s32 id, max = (__s32)btf__get_nr_types(btf); + __s32 id, max = (__s32)type_cnt(btf); std::string type = std::string(""); - struct btf_dump_opts opts = { - .ctx = &type, - }; struct btf_dump *dump; std::string funcs; char err_buf[256]; int err; - dump = btf_dump__new(btf, nullptr, &opts, dump_printf); + dump = dump_new(btf, dump_printf, &type); err = libbpf_get_error(dump); if (err) { @@ -509,6 +541,9 @@ { const struct btf_type *t = btf__type_by_id(btf, id); + if (!t) + continue; + if (!btf_is_func(t)) continue; @@ -516,7 +551,7 @@ std::string func_name = str; t = btf__type_by_id(btf, t->type); - if (!btf_is_func_proto(t)) + if (!t || !btf_is_func_proto(t)) { /* bad.. */ if (!bt_verbose) @@ -545,16 +580,13 @@ const std::set &funcs) const { #ifdef HAVE_LIBBPF_BTF_DUMP_EMIT_TYPE_DECL - __s32 id, max = (__s32)btf__get_nr_types(btf); + __s32 id, max = (__s32)type_cnt(btf); std::string type = std::string(""); - struct btf_dump_opts opts = { - .ctx = &type, - }; struct btf_dump *dump; char err_buf[256]; int err; - dump = btf_dump__new(btf, nullptr, &opts, dump_printf); + dump = dump_new(btf, dump_printf, &type); err = libbpf_get_error(dump); if (err) { @@ -568,6 +600,9 @@ { const struct btf_type *t = btf__type_by_id(btf, id); + if (!t) + continue; + if (!btf_is_func(t)) continue; @@ -578,6 +613,8 @@ continue; t = btf__type_by_id(btf, t->type); + if (!t) + continue; _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") @@ -631,7 +668,7 @@ return params; #else LOG(ERROR) << "Could not get kfunc arguments " - "(HAVE_LIBBPF_BTF_DUMP_EMIT_TYPE_DECL is not set)" return {}; + "(HAVE_LIBBPF_BTF_DUMP_EMIT_TYPE_DECL is not set)"; return {}; #endif } @@ -639,16 +676,13 @@ std::set BTF::get_all_structs() const { std::set struct_set; - __s32 id, max = (__s32)btf__get_nr_types(btf); + __s32 id, max = (__s32)type_cnt(btf); std::string types = std::string(""); - struct btf_dump_opts opts = { - .ctx = &types, - }; struct btf_dump *dump; char err_buf[256]; int err; - dump = btf_dump__new(btf, nullptr, &opts, dump_printf); + dump = dump_new(btf, dump_printf, &types); err = libbpf_get_error(dump); if (err) { @@ -661,7 +695,7 @@ { const struct btf_type *t = btf__type_by_id(btf, id); - if (!(btf_is_struct(t) || btf_is_union(t) || btf_is_enum(t))) + if (!t || !(btf_is_struct(t) || btf_is_union(t) || btf_is_enum(t))) continue; const std::string name = full_type_str(btf, t); diff -Nru bpftrace-0.14.1/src/child.cpp bpftrace-0.15.0/src/child.cpp --- bpftrace-0.14.1/src/child.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/child.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -174,7 +174,7 @@ return; if (child_pid_ <= 1) - throw std::runtime_error("BUG: child_pid <= 1"); + LOG(BUG) << "child_pid <= 1"; int sig = force ? SIGKILL : SIGTERM; @@ -287,7 +287,7 @@ if (ret < 0) { if (errno == EINVAL) - throw std::runtime_error("BUG: waitpid() EINVAL"); + LOG(BUG) << "waitpid() EINVAL"; else { LOG(ERROR) << "waitpid(" << child_pid_ diff -Nru bpftrace-0.14.1/src/clang_parser.cpp bpftrace-0.15.0/src/clang_parser.cpp --- bpftrace-0.14.1/src/clang_parser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/clang_parser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -565,7 +565,14 @@ // Initialize a new record type if needed if (!structs.Has(ptypestr)) - structs.Add(ptypestr, ptypesize); + structs.Add(ptypestr, ptypesize, false); + + auto str = structs.Lookup(ptypestr).lock(); + if (str->allow_override) + { + str->ClearFields(); + str->allow_override = false; + } // No need to worry about redefined types b/c we should have already // checked clang diagnostics. The diagnostics will tell us if we have @@ -715,13 +722,17 @@ .Length = input.size(), }); - // clang-format off - args = { - "-isystem", "/usr/local/include", - "-isystem", "/bpftrace/include", - "-isystem", "/usr/include", - }; - // clang-format on + args = { "-isystem", "/bpftrace/include" }; + auto system_paths = system_include_paths(); + for (auto &path : system_paths) + { + args.push_back("-isystem"); + args.push_back(path.c_str()); + } + std::string arch_path = get_arch_include_path(); + args.push_back("-isystem"); + args.push_back(arch_path.c_str()); + for (auto &flag : extra_flags) { args.push_back(flag.c_str()); @@ -748,6 +759,9 @@ // Since we're omitting there's no reason to // add the wokarounds for it args.push_back("-D__CLANG_WORKAROUNDS_H"); + // Let script know we have BTF -- this is useful for prewritten tools to + // conditionally include headers if BTF isn't available. + args.push_back("-DBPFTRACE_HAVE_BTF"); if (handler.parse_file("definitions.h", input, args, input_files, false) && handler.has_redefinition_error()) @@ -780,6 +794,7 @@ // taken from BTF. We cannot use BTF in such a case. args.pop_back(); args.pop_back(); + args.pop_back(); input_files.back() = get_empty_btf_generated_header(); } @@ -882,4 +897,38 @@ }; } +std::string ClangParser::get_arch_include_path() +{ + struct utsname utsname; + uname(&utsname); + return "/usr/include/" + std::string(utsname.machine) + "-linux-gnu"; +} + +std::vector ClangParser::system_include_paths() +{ + std::vector result; + try + { + auto clang = "clang-" + std::to_string(LLVM_VERSION_MAJOR); + auto cmd = clang + " -Wp,-v -x c -fsyntax-only /dev/null 2>&1"; + auto check = exec_system(cmd.c_str()); + std::istringstream lines(check); + std::string line; + while (std::getline(lines, line) && + line != "#include <...> search starts here:") + { + } + while (std::getline(lines, line) && line != "End of search list.") + result.push_back(trim(line)); + } + catch (std::runtime_error &) + { // If exec_system fails, just ignore it + } + + if (result.empty()) + result = { "/usr/local/include", "/usr/include" }; + + return result; +} + } // namespace bpftrace diff -Nru bpftrace-0.14.1/src/clang_parser.h bpftrace-0.15.0/src/clang_parser.h --- bpftrace-0.14.1/src/clang_parser.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/clang_parser.h 2022-05-24 10:41:38.000000000 +0000 @@ -58,6 +58,9 @@ CXUnsavedFile get_btf_generated_header(BPFtrace &bpftrace); CXUnsavedFile get_empty_btf_generated_header(); + std::string get_arch_include_path(); + std::vector system_include_paths(); + std::string input; std::vector args; std::vector input_files; diff -Nru bpftrace-0.14.1/src/CMakeLists.txt bpftrace-0.15.0/src/CMakeLists.txt --- bpftrace-0.14.1/src/CMakeLists.txt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/CMakeLists.txt 2022-05-24 10:41:38.000000000 +0000 @@ -22,6 +22,7 @@ disasm.cpp dwarf_parser.cpp fake_map.cpp + format_string.cpp imap.cpp log.cpp map.cpp diff -Nru bpftrace-0.14.1/src/driver.cpp bpftrace-0.15.0/src/driver.cpp --- bpftrace-0.14.1/src/driver.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/driver.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -17,11 +17,6 @@ { } -Driver::~Driver() -{ - delete root_; -} - void Driver::source(std::string filename, std::string script) { Log::get().set_source(filename, script); @@ -36,10 +31,8 @@ int Driver::parse() { - // Ensure we free memory allocated the previous parse if we parse - // more than once - delete root_; - root_ = nullptr; + // Reset previous state if we parse more than once + root.reset(); // Reset source location info on every pass loc.initialize(); @@ -57,15 +50,14 @@ if (!failed_) { - ast::AttachPointParser ap_parser(root_, bpftrace_, out_, listing_); + ast::AttachPointParser ap_parser(root.get(), bpftrace_, out_, listing_); if (ap_parser.parse()) failed_ = true; } if (failed_) { - delete root_; - root_ = nullptr; + root.reset(); } // Keep track of errors thrown ourselves, since the result of diff -Nru bpftrace-0.14.1/src/driver.h bpftrace-0.15.0/src/driver.h --- bpftrace-0.14.1/src/driver.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/driver.h 2022-05-24 10:41:38.000000000 +0000 @@ -14,7 +14,6 @@ { public: explicit Driver(BPFtrace &bpftrace, std::ostream &o = std::cerr); - ~Driver(); int parse(); int parse_str(std::string script); @@ -22,7 +21,7 @@ void error(std::ostream &, const location &, const std::string &); void error(const location &l, const std::string &m); void error(const std::string &m); - ast::Program *root_ = nullptr; + std::unique_ptr root; void debug() { diff -Nru bpftrace-0.14.1/src/dwarf_parser.cpp bpftrace-0.15.0/src/dwarf_parser.cpp --- bpftrace-0.14.1/src/dwarf_parser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/dwarf_parser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -2,6 +2,7 @@ #ifdef HAVE_LIBDW +#include "bpftrace.h" #include "log.h" #include @@ -15,7 +16,8 @@ Dwarf_Die die; }; -Dwarf::Dwarf(const std::string &file_path) : file_path_(file_path) +Dwarf::Dwarf(BPFtrace *bpftrace, const std::string &file_path) + : bpftrace_(bpftrace), file_path_(file_path) { callbacks.find_debuginfo = dwfl_standard_find_debuginfo; callbacks.section_address = dwfl_offline_section_address; @@ -25,9 +27,10 @@ dwfl_report_end(dwfl, NULL, NULL); } -std::unique_ptr Dwarf::GetFromBinary(const std::string &file_path) +std::unique_ptr Dwarf::GetFromBinary(BPFtrace *bpftrace, + const std::string &file_path) { - std::unique_ptr dwarf(new Dwarf(file_path)); + std::unique_ptr dwarf(new Dwarf(bpftrace, file_path)); Dwarf_Addr bias; if (dwfl_nextcu(dwarf->dwfl, NULL, &bias) == NULL) return nullptr; @@ -81,19 +84,7 @@ if (!func_die) return {}; - Dwarf_Die param_die; - Dwarf_Die *param_iter = ¶m_die; - if (dwarf_child(&func_die.value(), ¶m_die) != 0) - return {}; - - std::vector param_dies; - do - { - if (dwarf_tag(¶m_die) == DW_TAG_formal_parameter) - param_dies.push_back(param_die); - } while (dwarf_siblingof(param_iter, ¶m_die) == 0); - - return param_dies; + return get_all_children_with_tag(&func_die.value(), DW_TAG_formal_parameter); } std::string Dwarf::get_type_name(Dwarf_Die &type_die) const @@ -140,7 +131,17 @@ } } -SizedType Dwarf::get_stype(Dwarf_Die &type_die) const +Dwarf_Word Dwarf::get_type_encoding(Dwarf_Die &type_die) const +{ + Dwarf_Attribute encoding_attr; + Dwarf_Word encoding; + dwarf_formudata( + dwarf_attr_integrate(&type_die, DW_AT_encoding, &encoding_attr), + &encoding); + return encoding; +} + +SizedType Dwarf::get_stype(Dwarf_Die &type_die, bool resolve_structs) const { Dwarf_Die type; dwarf_peel_type(&type_die, &type); @@ -152,11 +153,7 @@ switch (tag) { case DW_TAG_base_type: { - Dwarf_Attribute encoding_attr; - Dwarf_Word encoding; - dwarf_formudata( - dwarf_attr_integrate(&type, DW_AT_encoding, &encoding_attr), - &encoding); + Dwarf_Word encoding = get_type_encoding(type); switch (encoding) { case DW_ATE_boolean: @@ -176,16 +173,93 @@ if (dwarf_hasattr(&type, DW_AT_type)) { Dwarf_Die inner_type = type_of(type); - return CreatePointer(get_stype(inner_type)); + return CreatePointer(get_stype(inner_type, false)); } // void * return CreatePointer(CreateNone()); } + case DW_TAG_structure_type: + case DW_TAG_union_type: { + std::string name = dwarf_diename(&type_die); + name = (tag == DW_TAG_structure_type ? "struct " : "union ") + name; + auto result = CreateRecord( + name, bpftrace_->structs.LookupOrAdd(name, bit_size / 8)); + if (resolve_structs) + resolve_fields(result); + return result; + } + case DW_TAG_array_type: { + Dwarf_Die inner_type_die = type_of(type_die); + Dwarf_Word inner_enc = get_type_encoding(inner_type_die); + SizedType inner_type = get_stype(inner_type_die); + SizedType result; + for (auto &d : get_all_children_with_tag(&type_die, DW_TAG_subrange_type)) + { + ssize_t size = get_array_size(d); + if (dwarf_tag(&inner_type_die) == DW_TAG_base_type && + (inner_enc == DW_ATE_signed_char || + inner_enc == DW_ATE_unsigned_char)) + result = CreateString(size); + else + result = CreateArray(size, inner_type); + inner_type = result; + } + return result; + } default: return CreateNone(); } } +SizedType Dwarf::get_stype(const std::string &type_name) const +{ + std::string name = type_name; + if (name.find("struct ") == 0) + name = name.substr(strlen("struct ")); + + auto type_die = find_type(name); + if (!type_die) + return CreateNone(); + + return get_stype(type_die.value()); +} + +void Dwarf::resolve_fields(const SizedType &type) const +{ + if (!type.IsRecordTy()) + return; + + auto str = bpftrace_->structs.Lookup(type.GetName()).lock(); + if (str->HasFields()) + return; + + std::string type_name = type.GetName(); + if (type_name.find("struct ") == 0) + type_name = type_name.substr(strlen("struct ")); + auto type_die = find_type(type_name); + if (!type_die) + return; + + for (auto &field_die : + get_all_children_with_tag(&type_die.value(), DW_TAG_member)) + { + if (dwarf_hasattr(&field_die, DW_AT_bit_size)) + { + // Parsing bitfields from DWARF is not supported, yet -> clear the struct + // and let Clang parser resolve the fields. + str->ClearFields(); + break; + } + Dwarf_Die field_type = type_of(field_die); + str->AddField(dwarf_diename(&field_die), + get_stype(field_type), + get_field_offset(field_die), + false, + {}, + false); + } +} + std::vector Dwarf::get_function_params( const std::string &function) const { @@ -214,6 +288,98 @@ return result; } +std::optional Dwarf::find_type(const std::string &name) const +{ + Dwarf_Die *cudie = nullptr; + Dwarf_Addr cubias; + while ((cudie = dwfl_nextcu(dwfl, cudie, &cubias)) != nullptr) + { + if (auto type_die = get_child_with_tagname(cudie, + DW_TAG_structure_type, + name)) + return type_die; + } + return std::nullopt; +} + +std::optional Dwarf::get_child_with_tagname(Dwarf_Die *die, + int tag, + const std::string &name) +{ + Dwarf_Die child_die; + Dwarf_Die *child_iter = &child_die; + if (dwarf_child(die, &child_die) != 0) + return std::nullopt; + + do + { + if (dwarf_tag(&child_die) == tag && dwarf_hasattr(&child_die, DW_AT_name) && + dwarf_diename(&child_die) == name) + return child_die; + } while (dwarf_siblingof(child_iter, &child_die) == 0); + + return std::nullopt; +} + +std::vector Dwarf::get_all_children_with_tag(Dwarf_Die *die, int tag) +{ + Dwarf_Die child_die; + Dwarf_Die *child_iter = &child_die; + if (dwarf_child(die, &child_die) != 0) + return {}; + + std::vector children; + do + { + if (dwarf_tag(&child_die) == tag) + children.push_back(child_die); + } while (dwarf_siblingof(child_iter, &child_die) == 0); + + return children; +} + +ssize_t Dwarf::get_array_size(Dwarf_Die &subrange_die) +{ + Dwarf_Attribute size_attr; + Dwarf_Word size; + if (dwarf_hasattr(&subrange_die, DW_AT_upper_bound)) + { + dwarf_formudata( + dwarf_attr_integrate(&subrange_die, DW_AT_upper_bound, &size_attr), + &size); + return (ssize_t)size + 1; + } + if (dwarf_hasattr(&subrange_die, DW_AT_count)) + { + dwarf_formudata( + dwarf_attr_integrate(&subrange_die, DW_AT_count, &size_attr), &size); + return (ssize_t)size; + } + return 0; +} + +ssize_t Dwarf::get_field_offset(Dwarf_Die &field_die) +{ + Dwarf_Attribute attr; + Dwarf_Word value; + if (dwarf_hasattr(&field_die, DW_AT_data_member_location)) + { + if (dwarf_formudata( + dwarf_attr_integrate(&field_die, DW_AT_data_member_location, &attr), + &value) >= 0) + return (ssize_t)value; + } + if (dwarf_hasattr(&field_die, DW_AT_data_bit_offset)) + { + if (dwarf_formudata( + dwarf_attr_integrate(&field_die, DW_AT_data_bit_offset, &attr), + &value) >= 0) + return (ssize_t)value; + } + + return 0; +} + } // namespace bpftrace -#endif // HAVE_LIBDW \ No newline at end of file +#endif // HAVE_LIBDW diff -Nru bpftrace-0.14.1/src/dwarf_parser.h bpftrace-0.15.0/src/dwarf_parser.h --- bpftrace-0.14.1/src/dwarf_parser.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/dwarf_parser.h 2022-05-24 10:41:38.000000000 +0000 @@ -20,27 +20,40 @@ public: virtual ~Dwarf(); - static std::unique_ptr GetFromBinary(const std::string &file_path); + static std::unique_ptr GetFromBinary(BPFtrace *bpftrace, + const std::string &file_path); std::vector get_function_params( const std::string &function) const; - ProbeArgs resolve_args(const std::string &function); + SizedType get_stype(const std::string &type_name) const; + void resolve_fields(const SizedType &type) const; + private: - explicit Dwarf(const std::string &file_path); + Dwarf(BPFtrace *bpftrace, const std::string &file_path); std::vector function_param_dies(const std::string &function) const; - std::optional get_func_die(const std::string &function) const; - std::string get_type_name(Dwarf_Die &type_die) const; - - SizedType get_stype(Dwarf_Die &type_die) const; + Dwarf_Word get_type_encoding(Dwarf_Die &type_die) const; + std::optional find_type(const std::string &name) const; + static ssize_t get_array_size(Dwarf_Die &subrange_die); + static ssize_t get_field_offset(Dwarf_Die &field_die); + + SizedType get_stype(Dwarf_Die &type_die, bool resolve_structs = true) const; + + static std::optional get_child_with_tagname( + Dwarf_Die *die, + int tag, + const std::string &name); + static std::vector get_all_children_with_tag(Dwarf_Die *die, + int tag); Dwfl *dwfl = nullptr; Dwfl_Callbacks callbacks; + BPFtrace *bpftrace_; std::string file_path_; }; @@ -52,10 +65,14 @@ namespace bpftrace { +class BPFtrace; + class Dwarf { public: - static std::unique_ptr GetFromBinary(const std::string &file_path_ + static std::unique_ptr GetFromBinary(BPFtrace *bpftrace + __attribute__((unused)), + const std::string &file_path_ __attribute__((unused))) { static bool warned = false; @@ -75,6 +92,16 @@ { return {}; } + + SizedType get_stype(const std::string &type_name + __attribute__((unused))) const + { + return CreateNone(); + } + + void resolve_fields(const SizedType &type __attribute__((unused))) const + { + } }; } // namespace bpftrace diff -Nru bpftrace-0.14.1/src/format_string.cpp bpftrace-0.15.0/src/format_string.cpp --- bpftrace-0.14.1/src/format_string.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/src/format_string.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,167 @@ +#include "format_string.h" +#include "log.h" +#include "struct.h" + +namespace bpftrace { + +const int FMT_BUF_SZ = 512; + +std::string validate_format_string(const std::string &fmt, + std::vector args) +{ + std::stringstream message; + + auto tokens_begin = std::sregex_iterator(fmt.begin(), + fmt.end(), + format_specifier_re); + auto tokens_end = std::sregex_iterator(); + + auto num_tokens = std::distance(tokens_begin, tokens_end); + int num_args = args.size(); + if (num_args < num_tokens) + { + message << "printf: Not enough arguments for format string (" << num_args + << " supplied, " << num_tokens << " expected)" << std::endl; + return message.str(); + } + if (num_args > num_tokens) + { + message << "printf: Too many arguments for format string (" << num_args + << " supplied, " << num_tokens << " expected)" << std::endl; + return message.str(); + } + + auto token_iter = tokens_begin; + for (int i = 0; i < num_args; i++, token_iter++) + { + Type arg_type = args.at(i).type.type; + if (arg_type == Type::ksym || arg_type == Type::usym || + arg_type == Type::probe || arg_type == Type::username || + arg_type == Type::kstack || arg_type == Type::ustack || + arg_type == Type::inet || arg_type == Type::timestamp || + arg_type == Type::mac_address || arg_type == Type::cgroup_path) + arg_type = Type::string; // Symbols should be printed as strings + if (arg_type == Type::pointer) + arg_type = Type::integer; // Casts (pointers) can be printed as integers + int offset = 1; + + // skip over format widths during verification + if (token_iter->str()[offset] == '-') + offset++; + while ((token_iter->str()[offset] >= '0' && + token_iter->str()[offset] <= '9') || + token_iter->str()[offset] == '.') + offset++; + + const std::string token = token_iter->str().substr(offset); + const auto token_type_iter = printf_format_types.find(token); + if (token_type_iter == printf_format_types.end()) + { + message << "printf: Unknown format string token: %" << token << std::endl; + return message.str(); + } + const Type &token_type = token_type_iter->second; + + if (arg_type != token_type) + { + message << "printf: %" << token << " specifier expects a value of type " + << token_type << " (" << arg_type << " supplied)" << std::endl; + return message.str(); + } + } + return ""; +} + +void FormatString::split() +{ + auto tokens_begin = std::sregex_iterator(fmt_.begin(), + fmt_.end(), + format_specifier_re); + auto tokens_end = std::sregex_iterator(); + + size_t last_pos = 0; + for (std::regex_iterator i = tokens_begin; i != tokens_end; i++) + { + int end = i->position() + i->length(); + parts_.push_back(fmt_.substr(last_pos, end - last_pos)); + last_pos = end; + } + + if (last_pos != fmt_.length()) + { + parts_.push_back(fmt_.substr(last_pos)); + } +} + +void FormatString::format(std::ostream &out, + std::vector> &args) +{ + if (parts_.size() < 1) + { + split(); + } + auto buffer = std::vector(FMT_BUF_SZ); + auto check_snprintf_ret = [](int r) { + if (r < 0) + { + LOG(FATAL) << "format() error occurred: " << std::strerror(errno); + } + }; + + size_t i = 0; + for (; i < args.size(); i++) + { + for (int try_ = 0; try_ < 2; try_++) + { + // find format specified in the string + auto last_percent_sign = parts_[i].find_last_of('%'); + std::string fmt_string = last_percent_sign != std::string::npos + ? parts_[i].substr(last_percent_sign) + : ""; + std::string printf_fmt; + if (fmt_string == "%r" || fmt_string == "%rx") + { + if (fmt_string == "%rx") + { + auto printable_buffer = dynamic_cast(&*args.at(i)); + // this is checked by semantic analyzer + assert(printable_buffer); + printable_buffer->keep_ascii(false); + } + // replace nonstandard format specifier with %s + printf_fmt = std::regex_replace(parts_[i], std::regex("%rx?"), "%s"); + } + else + { + printf_fmt = parts_[i]; + } + int r = args.at(i)->print(buffer.data(), + buffer.capacity(), + printf_fmt.c_str()); + check_snprintf_ret(r); + if (static_cast(r) < buffer.capacity()) + // string fits into buffer, we are done + break; + else + // the buffer is not big enough to hold the string, resize it + // and try again + buffer.resize(r + 1); + } + + out << buffer.data(); + } + if (i < parts_.size()) + { + out << parts_[i]; + } +} + +std::string FormatString::format_str( + std::vector> &args) +{ + std::stringstream buf; + format(buf, args); + return buf.str(); +} + +} // namespace bpftrace diff -Nru bpftrace-0.14.1/src/format_string.h bpftrace-0.15.0/src/format_string.h --- bpftrace-0.14.1/src/format_string.h 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/src/format_string.h 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include + +#include "printf.h" + +namespace bpftrace { + +/** + * validate_fmt makes sure that the type are valid for the format specifiers + */ +std::string validate_format_string(const std::string &fmt, + std::vector args); + +struct Field; +/* +** +*/ +class FormatString +{ +private: + /** + * Split the format string on format specifiers, e.g. + * 'foo %s bar' -> [ 'foo %s', 'bar' ] + */ + void split(); + +public: + /* + * NOTE: As format strings are used as a vector of tuples the cereal + * serialization can get hairy. Having a public constructor makes it easier. + */ + FormatString() = default; + + FormatString(const char *s) : fmt_(s){}; + FormatString(std::string &s) : fmt_(s){}; + + /** + * format formats the format string with the given args. Its up to the caller + * to ensure that the argument types match those of the call to validate_types + */ + void format(std::ostream &out, + std::vector> &args); + + /** + * format_str is similar to format but returns a string instead of writing to + * an ostream + */ + std::string format_str(std::vector> &args); + + /** + * length returns the length of the format string + */ + inline size_t length() const noexcept + { + return fmt_.length(); + }; + inline size_t size() const noexcept + { + return length(); + }; + + /** + * str returns the format string as std::string + */ + inline std::string str() const + { + return fmt_; + }; + + /** + * c_str returns the format string as c string + * */ + inline const char *c_str() const noexcept + { + return fmt_.c_str(); + }; + +private: + std::string fmt_; + std::vector parts_; + + friend class cereal::access; + + template + void serialize(Archive &ar) + { + // NOTE: parts_ is not constructed until first use, so no point in + // serializing it + ar(fmt_); + } +}; + +} // namespace bpftrace diff -Nru bpftrace-0.14.1/src/fuzz_main.cpp bpftrace-0.15.0/src/fuzz_main.cpp --- bpftrace-0.14.1/src/fuzz_main.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/fuzz_main.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -17,12 +17,12 @@ #include #include "ast/bpforc/bpforc.h" -#include "ast/clang_parser.h" #include "ast/passes/callback_visitor.h" +#include "ast/passes/codegen_llvm.h" #include "ast/passes/field_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpftrace.h" -#include "codegen_llvm.h" +#include "clang_parser.h" #include "driver.h" #include "log.h" #include "output.h" @@ -114,18 +114,18 @@ uint64_t node_count = 0; ast::CallbackVisitor counter( [&](ast::Node* node __attribute__((unused))) { node_count += 1; }); - driver.root_->accept(counter); + driver.root->accept(counter); if (node_count > node_max) return 1; // Field Analyzer - ast::FieldAnalyser fields(driver.root_, bpftrace, devnull); + ast::FieldAnalyser fields(driver.root.get(), bpftrace, devnull); err = fields.analyse(); if (err) return err; // Tracepoint parser - if (TracepointFormatParser::parse(driver.root_, bpftrace) == false) + if (TracepointFormatParser::parse(driver.root.get(), bpftrace) == false) return 1; // ClangParser @@ -135,7 +135,7 @@ struct utsname utsname; uname(&utsname); std::string ksrc, kobj; - auto kdirs = get_kernel_dirs(utsname, !bpftrace.features_->has_btf()); + auto kdirs = get_kernel_dirs(utsname, !bpftrace.feature_->has_btf()); ksrc = std::get<0>(kdirs); kobj = std::get<1>(kdirs); @@ -144,14 +144,14 @@ } extra_flags.push_back("-include"); extra_flags.push_back(CLANG_WORKAROUNDS_H); - if (!clang.parse(driver.root_, bpftrace, extra_flags)) + if (!clang.parse(driver.root.get(), bpftrace, extra_flags)) return 1; err = driver.parse(); if (err) return err; // Semantic Analyzer - ast::SemanticAnalyser semantics(driver.root_, bpftrace, devnull, false); + ast::SemanticAnalyser semantics(driver.root.get(), bpftrace, devnull, false); err = semantics.analyse(); if (err) return err; @@ -166,7 +166,7 @@ return err; // Codegen - ast::CodegenLLVM llvm(driver.root_, bpftrace); + ast::CodegenLLVM llvm(driver.root.get(), bpftrace); std::unique_ptr bpforc; try { diff -Nru bpftrace-0.14.1/src/lexer.l bpftrace-0.15.0/src/lexer.l --- bpftrace-0.14.1/src/lexer.l 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/lexer.l 2022-05-24 10:41:38.000000000 +0000 @@ -38,7 +38,7 @@ space {hspace}|{vspace} path :(\\.|[_\-\./a-zA-Z0-9#\*])+ builtin arg[0-9]|args|cgroup|comm|cpid|cpu|ctx|curtask|elapsed|func|gid|nsecs|pid|probe|rand|retval|sarg[0-9]|tid|uid|username -call avg|buf|cat|cgroupid|clear|count|delete|exit|hist|join|kaddr|kptr|ksym|lhist|macaddr|max|min|ntop|override|print|printf|reg|signal|sizeof|stats|str|strftime|strncmp|sum|system|time|uaddr|uptr|usym|zero|path|unwatch +call avg|buf|cat|cgroupid|clear|count|delete|exit|hist|join|kaddr|kptr|ksym|lhist|macaddr|max|min|ntop|override|print|printf|cgroup_path|reg|signal|sizeof|stats|str|strftime|strncmp|sum|system|time|uaddr|uptr|usym|zero|path|unwatch|bswap /* Don't add to this! Use builtin OR call not both */ call_and_builtin kstack|ustack diff -Nru bpftrace-0.14.1/src/log.cpp bpftrace-0.15.0/src/log.cpp --- bpftrace-0.14.1/src/log.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/log.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -12,7 +12,8 @@ case LogType::WARNING : return "WARNING"; case LogType::ERROR : return "ERROR"; case LogType::FATAL : return "FATAL"; - // clang-format on + case LogType::BUG : return "BUG"; + // clang-format on } return {}; // unreached @@ -25,6 +26,7 @@ enabled_map_[LogType::INFO] = true; enabled_map_[LogType::DEBUG] = true; enabled_map_[LogType::FATAL] = true; + enabled_map_[LogType::BUG] = true; } Log& Log::get() @@ -207,10 +209,7 @@ if (sink_.is_enabled(type_)) #endif { - std::string prefix = ""; - if (type_ == LogType::DEBUG) - prefix = "[" + log_file_ + ":" + std::to_string(log_line_) + "] "; - sink_.take_input(type_, loc_, out_, prefix + buf_.str()); + sink_.take_input(type_, loc_, out_, buf_.str()); } } @@ -220,4 +219,11 @@ abort(); } +[[noreturn]] LogStreamBug::~LogStreamBug() +{ + std::string prefix = "[" + log_file_ + ":" + std::to_string(log_line_) + "] "; + sink_.take_input(type_, loc_, out_, prefix + buf_.str()); + abort(); +} + }; // namespace bpftrace diff -Nru bpftrace-0.14.1/src/log.h bpftrace-0.15.0/src/log.h --- bpftrace-0.14.1/src/log.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/log.h 2022-05-24 10:41:38.000000000 +0000 @@ -17,7 +17,8 @@ INFO, WARNING, ERROR, - FATAL + FATAL, + BUG, }; // clang-format on @@ -118,6 +119,23 @@ [[noreturn]] ~LogStreamFatal(); }; +class LogStreamBug : public LogStream +{ +public: + LogStreamBug(const std::string& file, + int line, + __attribute__((unused)) LogType, + std::ostream& out = std::cerr) + : LogStream(file, line, LogType::BUG, out){}; + LogStreamBug(const std::string& file, + int line, + __attribute__((unused)) LogType, + const location& loc, + std::ostream& out = std::cerr) + : LogStream(file, line, LogType::BUG, loc, out){}; + [[noreturn]] ~LogStreamBug(); +}; + // Usage examples: // 1. LOG(WARNING) << "this is a " << "warning!"; (this goes to std::cerr) // 2. LOG(DEBUG, std::cout) << "this is a " << " message."; @@ -131,6 +149,7 @@ #define LOGSTREAM_WARNING(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_ERROR(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_FATAL(...) bpftrace::LogStreamFatal(__FILE__, __LINE__, __VA_ARGS__) +#define LOGSTREAM_BUG(...) bpftrace::LogStreamBug(__FILE__, __LINE__, __VA_ARGS__) // clang-format on #define LOG(type, ...) LOGSTREAM_##type(bpftrace::LogType::type, ##__VA_ARGS__) diff -Nru bpftrace-0.14.1/src/main.cpp bpftrace-0.15.0/src/main.cpp --- bpftrace-0.14.1/src/main.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/main.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -60,6 +60,22 @@ // Compile script into portable executable AHEAD_OF_TIME, }; + +enum Options +{ + INFO = 2000, + NO_WARNING, + TEST, + AOT, + HELP, + VERSION, + USDT_SEMAPHORE, + UNSAFE, + BTF, + INCLUDE, + EMIT_ELF, + EMIT_LLVM, +}; } // namespace void usage() @@ -74,8 +90,6 @@ std::cerr << " -B MODE output buffering mode ('full', 'none')" << std::endl; std::cerr << " -f FORMAT output format ('text', 'json')" << std::endl; std::cerr << " -o file redirect bpftrace output to file" << std::endl; - std::cerr << " -d debug info dry run" << std::endl; - std::cerr << " -dd verbose debug info dry run" << std::endl; std::cerr << " -e 'program' execute this program" << std::endl; std::cerr << " -h, --help show this help message" << std::endl; std::cerr << " -I DIR add the directory to the include search path" << std::endl; @@ -87,19 +101,26 @@ std::cerr << " activate usdt semaphores based on file path" << std::endl; std::cerr << " --unsafe allow unsafe builtin functions" << std::endl; std::cerr << " -q keep messages quiet" << std::endl; - std::cerr << " -v verbose messages" << std::endl; std::cerr << " --info Print information about kernel BPF support" << std::endl; std::cerr << " -k emit a warning when a bpf helper returns an error (except read functions)" << std::endl; std::cerr << " -kk check all bpf helper functions" << std::endl; std::cerr << " -V, --version bpftrace version" << std::endl; std::cerr << " --no-warnings disable all warning messages" << std::endl; std::cerr << std::endl; + std::cerr << "TROUBLESHOOTING OPTIONS:" << std::endl; + std::cerr << " -v verbose messages" << std::endl; + std::cerr << " -d (dry run) debug info" << std::endl; + std::cerr << " -dd (dry run) verbose debug info" << std::endl; + std::cerr << " --emit-elf FILE (dry run) generate ELF file with bpf programs and write to FILE" << std::endl; + std::cerr << " --emit-llvm FILE write LLVM IR to FILE.original.ll and FILE.optimized.ll" << std::endl; + std::cerr << std::endl; std::cerr << "ENVIRONMENT:" << std::endl; std::cerr << " BPFTRACE_STRLEN [default: 64] bytes on BPF stack per str()" << std::endl; std::cerr << " BPFTRACE_NO_CPP_DEMANGLE [default: 0] disable C++ symbol demangling" << std::endl; std::cerr << " BPFTRACE_MAP_KEYS_MAX [default: 4096] max keys in a map" << std::endl; std::cerr << " BPFTRACE_CAT_BYTES_MAX [default: 10k] maximum bytes read by cat builtin" << std::endl; std::cerr << " BPFTRACE_MAX_PROBES [default: 512] max number of probes" << std::endl; + std::cerr << " BPFTRACE_MAX_BPF_PROGS [default: 512] max number of generated BPF programs" << std::endl; std::cerr << " BPFTRACE_LOG_SIZE [default: 1000000] log size in bytes" << std::endl; std::cerr << " BPFTRACE_PERF_RB_PAGES [default: 64] pages per CPU to allocate for ring buffer" << std::endl; std::cerr << " BPFTRACE_NO_USER_SYMBOLS [default: 0] disable user symbol resolution" << std::endl; @@ -141,7 +162,7 @@ return true; } -static int info() +static void info() { struct utsname utsname; uname(&utsname); @@ -156,8 +177,6 @@ std::cerr << std::endl; std::cerr << BPFfeature().report(); - - return 0; } static std::optional get_boottime() @@ -268,6 +287,9 @@ if (!get_uint64_env_var("BPFTRACE_MAX_PROBES", bpftrace.max_probes_)) return false; + if (!get_uint64_env_var("BPFTRACE_MAX_BPF_PROGS", bpftrace.max_programs_)) + return false; + if (!get_uint64_env_var("BPFTRACE_LOG_SIZE", bpftrace.log_size_)) return false; @@ -276,10 +298,6 @@ if (!get_uint64_env_var("BPFTRACE_MAX_TYPE_RES_ITERATIONS", bpftrace.max_type_res_iterations)) - return 1; - - if (!get_uint64_env_var("BPFTRACE_MAX_TYPE_RES_ITERATIONS", - bpftrace.max_type_res_iterations)) return false; if (const char* env_p = std::getenv("BPFTRACE_CAT_BYTES_MAX")) @@ -355,12 +373,12 @@ if (err) return nullptr; - ast::FieldAnalyser fields(driver.root_, bpftrace); + ast::FieldAnalyser fields(driver.root.get(), bpftrace); err = fields.analyse(); if (err) return nullptr; - if (TracepointFormatParser::parse(driver.root_, bpftrace) == false) + if (TracepointFormatParser::parse(driver.root.get(), bpftrace) == false) return nullptr; ClangParser clang; @@ -394,19 +412,17 @@ // avoid issues in some versions. Since we're including files in the command // line, we want to force parsing, so we make sure C definitions are not // empty before going to clang parser stage. - if (!include_files.empty() && driver.root_->c_definitions.empty()) - driver.root_->c_definitions = "#define __BPFTRACE_DUMMY__"; + if (!include_files.empty() && driver.root->c_definitions.empty()) + driver.root->c_definitions = "#define __BPFTRACE_DUMMY__"; - if (!clang.parse(driver.root_, bpftrace, extra_flags)) + if (!clang.parse(driver.root.get(), bpftrace, extra_flags)) return nullptr; err = driver.parse(); if (err) return nullptr; - auto ast = driver.root_; - driver.root_ = nullptr; - return std::unique_ptr(ast); + return std::move(driver.root); } ast::PassManager CreateDynamicPM() @@ -429,9 +445,8 @@ return pm; } -int main(int argc, char* argv[]) +struct Args { - int err; std::string pid_str; std::string cmd_str; bool listing = false; @@ -439,68 +454,89 @@ bool usdt_file_activation = false; int helper_check_level = 0; TestMode test_mode = TestMode::UNSET; - std::string script, search, file_name, output_file, output_format, output_elf, - aot; + std::string script; + std::string search; + std::string filename; + std::string output_file; + std::string output_format; + std::string output_elf; + std::string output_llvm; + std::string aot; OutputBufferConfig obc = OutputBufferConfig::UNSET; BuildMode build_mode = BuildMode::DYNAMIC; - int c; + std::vector include_dirs; + std::vector include_files; + std::vector params; +}; + +Args parse_args(int argc, char* argv[]) +{ + Args args; const char* const short_options = "dbB:f:e:hlp:vqc:Vo:I:k"; option long_options[] = { - option{ "help", no_argument, nullptr, 'h' }, - option{ "version", no_argument, nullptr, 'V' }, - option{ "usdt-file-activation", no_argument, nullptr, '$' }, - option{ "unsafe", no_argument, nullptr, 'u' }, - option{ "btf", no_argument, nullptr, 'b' }, - option{ "include", required_argument, nullptr, '#' }, - option{ "info", no_argument, nullptr, 2000 }, - option{ "emit-elf", required_argument, nullptr, 2001 }, - option{ "no-warnings", no_argument, nullptr, 2002 }, - option{ "test", required_argument, nullptr, 2003 }, - option{ "aot", required_argument, nullptr, 2004 }, + option{ "help", no_argument, nullptr, Options::HELP }, + option{ "version", no_argument, nullptr, Options::VERSION }, + option{ + "usdt-file-activation", no_argument, nullptr, Options::USDT_SEMAPHORE }, + option{ "unsafe", no_argument, nullptr, Options::UNSAFE }, + option{ "btf", no_argument, nullptr, Options::BTF }, + option{ "include", required_argument, nullptr, Options::INCLUDE }, + option{ "info", no_argument, nullptr, Options::INFO }, + option{ "emit-llvm", required_argument, nullptr, Options::EMIT_LLVM }, + option{ "emit-elf", required_argument, nullptr, Options::EMIT_ELF }, + option{ "no-warnings", no_argument, nullptr, Options::NO_WARNING }, + option{ "test", required_argument, nullptr, Options::TEST }, + option{ "aot", required_argument, nullptr, Options::AOT }, option{ nullptr, 0, nullptr, 0 }, // Must be last }; - std::vector include_dirs; - std::vector include_files; + + int c; while ((c = getopt_long( argc, argv, short_options, long_options, nullptr)) != -1) { switch (c) { - case 2000: // --info + case Options::INFO: // --info if (is_root()) - return info(); - return 1; + { + info(); + exit(0); + } + exit(1); + break; + case Options::EMIT_ELF: // --emit-elf + args.output_elf = optarg; break; - case 2001: // --emit-elf - output_elf = optarg; + case Options::EMIT_LLVM: + args.output_llvm = optarg; break; - case 2002: // --no-warnings + case Options::NO_WARNING: // --no-warnings DISABLE_LOG(WARNING); break; - case 2003: // --test + case Options::TEST: // --test if (std::strcmp(optarg, "semantic") == 0) - test_mode = TestMode::SEMANTIC; + args.test_mode = TestMode::SEMANTIC; else if (std::strcmp(optarg, "codegen") == 0) - test_mode = TestMode::CODEGEN; + args.test_mode = TestMode::CODEGEN; else { LOG(ERROR) << "USAGE: --test must be either 'semantic' or 'codegen'."; - return 1; + exit(1); } break; - case 2004: // --aot - aot = optarg; - build_mode = BuildMode::AHEAD_OF_TIME; + case Options::AOT: // --aot + args.aot = optarg; + args.build_mode = BuildMode::AHEAD_OF_TIME; break; case 'o': - output_file = optarg; + args.output_file = optarg; break; case 'd': bt_debug++; if (bt_debug == DebugLevel::kNone) { usage(); - return 1; + exit(1); } break; case 'q': @@ -511,90 +547,157 @@ break; case 'B': if (std::strcmp(optarg, "line") == 0) { - obc = OutputBufferConfig::LINE; + args.obc = OutputBufferConfig::LINE; } else if (std::strcmp(optarg, "full") == 0) { - obc = OutputBufferConfig::FULL; + args.obc = OutputBufferConfig::FULL; } else if (std::strcmp(optarg, "none") == 0) { - obc = OutputBufferConfig::NONE; + args.obc = OutputBufferConfig::NONE; } else { LOG(ERROR) << "USAGE: -B must be either 'line', 'full', or 'none'."; - return 1; + exit(1); } break; case 'f': - output_format = optarg; + args.output_format = optarg; break; case 'e': - script = optarg; + args.script = optarg; break; case 'p': - pid_str = optarg; + args.pid_str = optarg; break; case 'I': - include_dirs.push_back(optarg); + args.include_dirs.push_back(optarg); break; - case '#': - include_files.push_back(optarg); + case Options::INCLUDE: + args.include_files.push_back(optarg); break; case 'l': - listing = true; + args.listing = true; break; case 'c': - cmd_str = optarg; + args.cmd_str = optarg; break; - case '$': - usdt_file_activation = true; + case Options::USDT_SEMAPHORE: + args.usdt_file_activation = true; break; - case 'u': - safe_mode = false; + case Options::UNSAFE: + args.safe_mode = false; break; case 'b': + case Options::BTF: break; case 'h': + case Options::HELP: usage(); - return 0; + exit(0); case 'V': + case Options::VERSION: std::cout << "bpftrace " << BPFTRACE_VERSION << std::endl; - return 0; + exit(0); case 'k': - helper_check_level++; - if (helper_check_level >= 3) + args.helper_check_level++; + if (args.helper_check_level >= 3) { usage(); - return 1; + exit(1); } break; default: usage(); - return 1; + exit(1); } } if (argc == 1) { usage(); - return 1; + exit(1); } if (bt_verbose && (bt_debug != DebugLevel::kNone)) { // TODO: allow both LOG(ERROR) << "USAGE: Use either -v or -d."; - return 1; + exit(1); } - if (!cmd_str.empty() && !pid_str.empty()) + if (!args.cmd_str.empty() && !args.pid_str.empty()) { LOG(ERROR) << "USAGE: Cannot use both -c and -p."; usage(); - return 1; + exit(1); } + // Difficult to serialize flex generated types + if (args.helper_check_level && args.build_mode == BuildMode::AHEAD_OF_TIME) + { + LOG(ERROR) << "Cannot use -k[k] with --aot"; + exit(1); + } + + if (args.listing) + { + // Expect zero or one positional arguments + if (optind == argc) + { + args.search = "*:*"; + } + else if (optind == argc - 1) + { + args.search = argv[optind]; + if (args.search == "*") + { + args.search = "*:*"; + } + optind++; + } + else + { + usage(); + exit(1); + } + } + else + { + // Expect to find a script either through -e or filename + if (args.script.empty() && argv[optind] == nullptr) + { + LOG(ERROR) << "USAGE: filename or -e 'program' required."; + exit(1); + } + + // If no script was specified with -e, then we expect to find a script file + if (args.script.empty()) + { + args.filename = argv[optind]; + optind++; + } + + // Load positional parameters before driver runs so positional + // parameters used inside attach point definitions can be resolved. + while (optind < argc) + { + args.params.push_back(argv[optind]); + optind++; + } + } + + return args; +} + +int main(int argc, char* argv[]) +{ + int err; + + const Args args = parse_args(argc, argv); + std::ostream * os = &std::cout; std::ofstream outputstream; - if (!output_file.empty()) { - outputstream.open(output_file); + if (!args.output_file.empty()) + { + outputstream.open(args.output_file); if (outputstream.fail()) { - LOG(ERROR) << "Failed to open output file: \"" << output_file + LOG(ERROR) << "Failed to open output file: \"" << args.output_file << "\": " << strerror(errno); return 1; } @@ -602,19 +705,22 @@ } std::unique_ptr output; - if (output_format.empty() || output_format == "text") { + if (args.output_format.empty() || args.output_format == "text") + { output = std::make_unique(*os); } - else if (output_format == "json") { + else if (args.output_format == "json") + { output = std::make_unique(*os); } else { - LOG(ERROR) << "Invalid output format \"" << output_format << "\"\n" + LOG(ERROR) << "Invalid output format \"" << args.output_format << "\"\n" << "Valid formats: 'text', 'json'"; return 1; } - switch (obc) { + switch (args.obc) + { case OutputBufferConfig::UNSET: case OutputBufferConfig::LINE: std::setvbuf(stdout, NULL, _IOLBF, BUFSIZ); @@ -629,29 +735,22 @@ BPFtrace bpftrace(std::move(output)); - if (!cmd_str.empty()) - bpftrace.cmd_ = cmd_str; + if (!args.cmd_str.empty()) + bpftrace.cmd_ = args.cmd_str; if (!parse_env(bpftrace)) return 1; - // Difficult to serialize flex generated types - if (helper_check_level && build_mode == BuildMode::AHEAD_OF_TIME) - { - LOG(ERROR) << "Cannot use -k[k] with --aot"; - return 1; - } - - bpftrace.usdt_file_activation_ = usdt_file_activation; - bpftrace.safe_mode_ = safe_mode; - bpftrace.helper_check_level_ = helper_check_level; + bpftrace.usdt_file_activation_ = args.usdt_file_activation; + bpftrace.safe_mode_ = args.safe_mode; + bpftrace.helper_check_level_ = args.helper_check_level; bpftrace.boottime_ = get_boottime(); - if (!pid_str.empty()) + if (!args.pid_str.empty()) { try { - bpftrace.procmon_ = std::make_unique(pid_str); + bpftrace.procmon_ = std::make_unique(args.pid_str); } catch (const std::exception& e) { @@ -660,12 +759,12 @@ } } - if (!cmd_str.empty()) + if (!args.cmd_str.empty()) { - bpftrace.cmd_ = cmd_str; + bpftrace.cmd_ = args.cmd_str; try { - bpftrace.child_ = std::make_unique(cmd_str); + bpftrace.child_ = std::make_unique(args.cmd_str); } catch (const std::runtime_error& e) { @@ -675,62 +774,45 @@ } // Listing probes - if (listing) + if (args.listing) { if (!is_root()) return 1; - if (optind == argc || std::string(argv[optind]) == "*") - script = "*:*"; - else if (optind == argc - 1) - script = argv[optind]; - else - { - usage(); - return 1; - } - - if (script.find(':') == std::string::npos && - (script.find("struct") == 0 || script.find("union") == 0 || - script.find("enum") == 0)) + if (args.search.find(':') == std::string::npos && + (args.search.find("struct") == 0 || args.search.find("union") == 0 || + args.search.find("enum") == 0)) { // Print structure definitions - bpftrace.probe_matcher_->list_structs(script); + bpftrace.probe_matcher_->list_structs(args.search); return 0; } Driver driver(bpftrace); driver.listing_ = true; - driver.source("stdin", script); + driver.source("stdin", args.search); int err = driver.parse(); if (err) return err; - ast::SemanticAnalyser semantics(driver.root_, bpftrace, false, true); + ast::SemanticAnalyser semantics(driver.root.get(), bpftrace, false, true); err = semantics.analyse(); if (err) return err; - bpftrace.probe_matcher_->list_probes(driver.root_); + bpftrace.probe_matcher_->list_probes(driver.root.get()); return 0; } std::string filename; std::string program; - if (script.empty()) + if (!args.filename.empty()) { - // Script file - if (argv[optind] == nullptr) - { - LOG(ERROR) << "USAGE: filename or -e 'program' required."; - return 1; - } - filename = argv[optind]; std::stringstream buf; - if (filename == "-") + if (args.filename == "-") { std::string line; while (std::getline(std::cin, line)) @@ -746,34 +828,30 @@ } else { - std::ifstream file(filename); + std::ifstream file(args.filename); if (file.fail()) { - LOG(ERROR) << "failed to open file '" << filename + LOG(ERROR) << "failed to open file '" << args.filename << "': " << std::strerror(errno); return -1; } + filename = args.filename; program = buf.str(); buf << file.rdbuf(); program = buf.str(); } - - optind++; } else { // Script is provided as a command line argument filename = "stdin"; - program = script; + program = args.script; } - // Load positional parameters before driver runs so positional - // parameters used inside attach point definitions can be resolved. - while (optind < argc) + for (const auto& param : args.params) { - bpftrace.add_param(argv[optind]); - optind++; + bpftrace.add_param(param); } if (!is_root()) @@ -791,19 +869,19 @@ enforce_infinite_rlimit(); auto ast_root = parse( - bpftrace, filename, program, include_dirs, include_files); + bpftrace, filename, program, args.include_dirs, args.include_files); if (!ast_root) return 1; ast::PassContext ctx(bpftrace); ast::PassManager pm; - switch (build_mode) + switch (args.build_mode) { case BuildMode::DYNAMIC: pm = CreateDynamicPM(); break; case BuildMode::AHEAD_OF_TIME: - pm = CreateAotPM(aot); + pm = CreateAotPM(args.aot); break; } @@ -817,7 +895,7 @@ { try { - bpftrace.child_ = std::make_unique(cmd_str); + bpftrace.child_ = std::make_unique(args.cmd_str); } catch (const std::runtime_error& e) { @@ -839,6 +917,10 @@ std::cout << "-------------------\n\n"; llvm.DumpIR(); } + if (!args.output_llvm.empty()) + { + llvm.DumpIR(args.output_llvm + ".original.ll"); + } llvm.optimize(); if (bt_debug != DebugLevel::kNone) @@ -850,9 +932,13 @@ } llvm.DumpIR(); } - if (!output_elf.empty()) + if (!args.output_llvm.empty()) { - llvm.emit_elf(output_elf); + llvm.DumpIR(args.output_llvm + ".optimized.ll"); + } + if (!args.output_elf.empty()) + { + llvm.emit_elf(args.output_elf); return 0; } bpforc = llvm.emit(); @@ -877,11 +963,11 @@ return 1; } - if (bt_debug != DebugLevel::kNone || test_mode == TestMode::CODEGEN) + if (bt_debug != DebugLevel::kNone || args.test_mode == TestMode::CODEGEN) return 0; - if (build_mode == BuildMode::AHEAD_OF_TIME) - return aot::generate(bpftrace.resources, bytecode, aot); + if (args.build_mode == BuildMode::AHEAD_OF_TIME) + return aot::generate(bpftrace.resources, bytecode, args.aot); // Signal handler that lets us know an exit signal was received. struct sigaction act = {}; @@ -889,6 +975,10 @@ sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); + // Signal handler that prints all maps when SIGUSR1 was received. + act.sa_handler = [](int) { BPFtrace::sigusr1_recv = true; }; + sigaction(SIGUSR1, &act, NULL); + err = bpftrace.run(bytecode); if (err) return err; diff -Nru bpftrace-0.14.1/src/output.cpp bpftrace-0.15.0/src/output.cpp --- bpftrace-0.14.1/src/output.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/output.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -275,6 +275,11 @@ ->nsecs_since_boot); else if (type.IsMacAddressTy()) return bpftrace.resolve_mac_address(value.data()); + else if (type.IsCgroupPathTy()) + return bpftrace.resolve_cgroup_path( + reinterpret_cast(value.data()) + ->cgroup_path_id, + reinterpret_cast(value.data())->cgroup_id); else return std::to_string(read_data(value.data()) / div); } diff -Nru bpftrace-0.14.1/src/parser.yy bpftrace-0.15.0/src/parser.yy --- bpftrace-0.14.1/src/parser.yy 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/parser.yy 2022-05-24 10:41:38.000000000 +0000 @@ -159,7 +159,7 @@ %% program: - c_definitions probes END { driver.root_ = new ast::Program($1, $2); } + c_definitions probes END { driver.root = std::make_unique($1, $2); } ; c_definitions: diff -Nru bpftrace-0.14.1/src/printf.cpp bpftrace-0.15.0/src/printf.cpp --- bpftrace-0.14.1/src/printf.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/printf.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -1,78 +1,26 @@ -#include - #include "printf.h" #include "printf_format_types.h" #include "struct.h" namespace bpftrace { -std::string verify_format_string(const std::string &fmt, std::vector args) +int PrintableString::print(char *buf, size_t size, const char *fmt) { - std::stringstream message; - - auto tokens_begin = std::sregex_iterator(fmt.begin(), - fmt.end(), - format_specifier_re); - auto tokens_end = std::sregex_iterator(); - - auto num_tokens = std::distance(tokens_begin, tokens_end); - int num_args = args.size(); - if (num_args < num_tokens) - { - message << "printf: Not enough arguments for format string (" << num_args - << " supplied, " << num_tokens << " expected)" << std::endl; - return message.str(); - } - if (num_args > num_tokens) - { - message << "printf: Too many arguments for format string (" << num_args - << " supplied, " << num_tokens << " expected)" << std::endl; - return message.str(); - } - - auto token_iter = tokens_begin; - for (int i=0; istr()[offset] == '-') - offset++; - while ((token_iter->str()[offset] >= '0' && token_iter->str()[offset] <= '9') || - token_iter->str()[offset] == '.') - offset++; - - const std::string token = token_iter->str().substr(offset); - const auto token_type_iter = printf_format_types.find(token); - if (token_type_iter == printf_format_types.end()) - { - message << "printf: Unknown format string token: %" << token << std::endl; - return message.str(); - } - const Type &token_type = token_type_iter->second; + return snprintf(buf, size, fmt, value_.c_str()); +} - if (arg_type != token_type) - { - message << "printf: %" << token << " specifier expects a value of type " - << token_type << " (" << arg_type << " supplied)" << std::endl; - return message.str(); - } - } - return ""; +int PrintableBuffer::print(char *buf, size_t size, const char *fmt) +{ + return snprintf( + buf, + size, + fmt, + hex_format_buffer(value_.data(), value_.size(), keep_ascii_).c_str()); } -int PrintableString::print(char *buf, size_t size, const char *fmt) +void PrintableBuffer::keep_ascii(bool value) { - return snprintf(buf, size, fmt, value_.c_str()); + keep_ascii_ = value; } int PrintableCString::print(char *buf, size_t size, const char *fmt) diff -Nru bpftrace-0.14.1/src/printf_format_types.h bpftrace-0.15.0/src/printf_format_types.h --- bpftrace-0.14.1/src/printf_format_types.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/printf_format_types.h 2022-05-24 10:41:38.000000000 +0000 @@ -12,52 +12,62 @@ // compile time string concatenation. here is the Python 3 code that generates this map: // #!/usr/bin/python3 // lengths = ("", "hh", "h", "l", "ll", "j", "z", "t") -// specifiers = ("d", "u", "x", "X", "p") +// specifiers = ("d", "u", "o", "x", "X", "p") // // print("{\"s\", Type::string},") // print("{\"r\", Type::buffer},") +// print("{\"c\", Type::integer},") // print(",\n".join([f"{{\"{l+s}\", Type::integer}}" for l in lengths for s in specifiers])) const std::unordered_map printf_format_types = { {"s", Type::string}, {"r", Type::buffer}, + {"rx", Type::buffer}, {"c", Type::integer}, {"d", Type::integer}, {"u", Type::integer}, + {"o", Type::integer}, {"x", Type::integer}, {"X", Type::integer}, {"p", Type::integer}, {"hhd", Type::integer}, {"hhu", Type::integer}, + {"hho", Type::integer}, {"hhx", Type::integer}, {"hhX", Type::integer}, {"hhp", Type::integer}, {"hd", Type::integer}, {"hu", Type::integer}, + {"ho", Type::integer}, {"hx", Type::integer}, {"hX", Type::integer}, {"hp", Type::integer}, {"ld", Type::integer}, {"lu", Type::integer}, + {"lo", Type::integer}, {"lx", Type::integer}, {"lX", Type::integer}, {"lp", Type::integer}, {"lld", Type::integer}, {"llu", Type::integer}, + {"llo", Type::integer}, {"llx", Type::integer}, {"llX", Type::integer}, {"llp", Type::integer}, {"jd", Type::integer}, {"ju", Type::integer}, + {"jo", Type::integer}, {"jx", Type::integer}, {"jX", Type::integer}, {"jp", Type::integer}, {"zd", Type::integer}, {"zu", Type::integer}, + {"zo", Type::integer}, {"zx", Type::integer}, {"zX", Type::integer}, {"zp", Type::integer}, {"td", Type::integer}, {"tu", Type::integer}, + {"to", Type::integer}, {"tx", Type::integer}, {"tX", Type::integer}, {"tp", Type::integer} diff -Nru bpftrace-0.14.1/src/printf.h bpftrace-0.15.0/src/printf.h --- bpftrace-0.14.1/src/printf.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/printf.h 2022-05-24 10:41:38.000000000 +0000 @@ -1,5 +1,6 @@ #pragma once +#include #include #include "ast/ast.h" @@ -24,9 +25,6 @@ struct Field; -std::string verify_format_string(const std::string& fmt, - std::vector args); - class IPrintable { public: @@ -44,6 +42,21 @@ std::string value_; }; +class PrintableBuffer : public virtual IPrintable +{ +public: + PrintableBuffer(char* buffer, size_t size) + : value_(std::vector(buffer, buffer + size)) + { + } + int print(char* buf, size_t size, const char* fmt) override; + void keep_ascii(bool value); + +private: + std::vector value_; + bool keep_ascii_ = true; +}; + class PrintableCString : public virtual IPrintable { public: diff -Nru bpftrace-0.14.1/src/probe_matcher.cpp bpftrace-0.15.0/src/probe_matcher.cpp --- bpftrace-0.14.1/src/probe_matcher.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/probe_matcher.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -35,25 +35,6 @@ #endif /* - * Splits input string by '*' delimiter and return the individual parts. - * Sets start_wildcard and end_wildcard if input starts or ends with '*'. - */ -std::vector get_tokens(const std::string& input, - bool& start_wildcard, - bool& end_wildcard) -{ - if (input.empty()) - return {}; - - start_wildcard = input[0] == '*'; - end_wildcard = input[input.length() - 1] == '*'; - - std::vector tokens = split_string(input, '*'); - tokens.erase(std::remove(tokens.begin(), tokens.end(), ""), tokens.end()); - return tokens; -} - -/* * Finds all matches of search_input in the provided input stream. * * If `ignore_trailing_module` is true, will ignore trailing kernel module. @@ -67,7 +48,7 @@ const char delim) { bool start_wildcard, end_wildcard; - auto tokens = get_tokens(search_input, start_wildcard, end_wildcard); + auto tokens = get_wildcard_tokens(search_input, start_wildcard, end_wildcard); std::string line; std::set matches; @@ -327,7 +308,7 @@ if (p.type == ProbeType::kfunc) { // kfunc must be available - if (bpftrace_->btf_.has_data()) + if (bpftrace_->feature_->has_prog_kfunc() && bpftrace_->btf_.has_data()) probes += p.name + "\n"; } else if (p.name.find("ret") == std::string::npos && @@ -430,7 +411,7 @@ { std::string fun = match; std::string path = erase_prefix(fun); - auto dwarf = Dwarf::GetFromBinary(path); + auto dwarf = Dwarf::GetFromBinary(nullptr, path); if (dwarf) params.emplace(match, dwarf->get_function_params(fun)); else @@ -502,6 +483,11 @@ case ProbeType::hardware: case ProbeType::software: { + // Do not expand "target:" as that would match all functions in target. + // This may occur when an absolute address is given instead of a function. + if (attach_point.func.empty()) + return { attach_point.target + ":" }; + search_input = attach_point.target + ":" + attach_point.func; break; } diff -Nru bpftrace-0.14.1/src/required_resources.cpp bpftrace-0.15.0/src/required_resources.cpp --- bpftrace-0.14.1/src/required_resources.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/required_resources.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -95,7 +95,7 @@ { // get size of all the format strings size_t size = 0; - for (auto it : seq_printf_args) + for (auto &it : seq_printf_args) size += std::get<0>(it).size() + 1; // compute buffer size to hold all the formats and create map with that diff -Nru bpftrace-0.14.1/src/required_resources.h bpftrace-0.15.0/src/required_resources.h --- bpftrace-0.14.1/src/required_resources.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/required_resources.h 2022-05-24 10:41:38.000000000 +0000 @@ -10,6 +10,7 @@ #include +#include "format_string.h" #include "location.hh" #include "mapkey.h" #include "mapmanager.h" @@ -74,13 +75,14 @@ void load_state(const uint8_t *ptr, size_t len); // Async argument metadata - std::vector>> system_args; - std::vector>> seq_printf_args; + std::vector>> system_args; + std::vector>> seq_printf_args; std::vector> seq_printf_ids; std::vector join_args; std::vector time_args; std::vector strftime_args; - std::vector>> cat_args; + std::vector cgroup_path_args; + std::vector>> cat_args; std::vector non_map_print_args; // Async argument metadata that codegen creates. Ideally ResourceAnalyser @@ -90,7 +92,7 @@ std::unordered_map helper_error_info; // `printf_args` is created here but the field offsets are fixed up // by codegen -- only codegen knows data layout to compute offsets - std::vector>> printf_args; + std::vector>> printf_args; std::vector probe_ids; // Map metadata diff -Nru bpftrace-0.14.1/src/struct.cpp bpftrace-0.15.0/src/struct.cpp --- bpftrace-0.14.1/src/struct.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/struct.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -113,12 +113,24 @@ .is_data_loc = is_data_loc }); } -void StructManager::Add(const std::string &name, size_t size) +bool Struct::HasFields() const +{ + return !fields.empty(); +} + +void Struct::ClearFields() +{ + fields.clear(); +} + +void StructManager::Add(const std::string &name, + size_t size, + bool allow_override) { if (struct_map_.find(name) != struct_map_.end()) throw std::runtime_error("Type redefinition: type with name \'" + name + "\' already exists"); - struct_map_[name] = std::make_unique(size); + struct_map_[name] = std::make_unique(size, allow_override); } std::weak_ptr StructManager::Lookup(const std::string &name) const @@ -128,9 +140,11 @@ } std::weak_ptr StructManager::LookupOrAdd(const std::string &name, - size_t size) + size_t size, + bool allow_override) { - auto s = struct_map_.insert({ name, std::make_unique(size) }); + auto s = struct_map_.insert( + { name, std::make_unique(size, allow_override) }); return s.first->second; } diff -Nru bpftrace-0.14.1/src/struct.h bpftrace-0.15.0/src/struct.h --- bpftrace-0.14.1/src/struct.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/struct.h 2022-05-24 10:41:38.000000000 +0000 @@ -73,8 +73,11 @@ bool padded = false; Fields fields; + bool allow_override = true; + Struct() = default; - explicit Struct(int size) : size(size) + explicit Struct(int size, bool allow_override = true) + : size(size), allow_override(allow_override) { } @@ -86,6 +89,8 @@ bool is_bitfield, const Bitfield &bitfield, bool is_data_loc); + bool HasFields() const; + void ClearFields(); static std::unique_ptr CreateTuple(std::vector fields); void Dump(std::ostream &os); @@ -148,9 +153,11 @@ { public: // struct map manipulation - void Add(const std::string &name, size_t size); + void Add(const std::string &name, size_t size, bool allow_override = true); std::weak_ptr Lookup(const std::string &name) const; - std::weak_ptr LookupOrAdd(const std::string &name, size_t size); + std::weak_ptr LookupOrAdd(const std::string &name, + size_t size, + bool allow_override = true); bool Has(const std::string &name) const; // tuples set manipulation diff -Nru bpftrace-0.14.1/src/tracepoint_format_parser.cpp bpftrace-0.15.0/src/tracepoint_format_parser.cpp --- bpftrace-0.14.1/src/tracepoint_format_parser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/tracepoint_format_parser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -17,11 +17,10 @@ { std::vector probes_with_tracepoint; for (ast::Probe *probe : *program->probes) - for (ast::AttachPoint *ap : *probe->attach_points) - if (ap->provider == "tracepoint") { - probes_with_tracepoint.push_back(probe); - continue; - } + { + if (probe->has_ap_of_probetype(ProbeType::tracepoint)) + probes_with_tracepoint.push_back(probe); + } if (probes_with_tracepoint.empty()) return true; @@ -108,18 +107,30 @@ // Errno might get clobbered by LOG(). int saved_errno = errno; - LOG(ERROR, ap->loc, std::cerr) - << "tracepoint not found: " << category << ":" << event_name; + // Do not fail if trying to attach to multiple tracepoints + // (at least one of them could succeed) + bool fail = probe->attach_points->size() == 1; + auto msg = "tracepoint not found: " + category + ":" + event_name; + if (fail) + LOG(ERROR, ap->loc, std::cerr) << msg; + else + LOG(WARNING, ap->loc, std::cerr) << msg; + // helper message: if (category == "syscall") LOG(WARNING, ap->loc, std::cerr) << "Did you mean syscalls:" << event_name << "?"; - if (bt_verbose) { + + if (fail && bt_verbose) + { // Having the location info isn't really useful here, so no // bpftrace.error LOG(ERROR) << strerror(saved_errno) << ": " << format_file_path; } - return false; + if (fail) + return false; + else + continue; } if (probe->tp_args_structs_level <= 0) diff -Nru bpftrace-0.14.1/src/types.cpp bpftrace-0.15.0/src/types.cpp --- bpftrace-0.14.1/src/types.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/types.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -121,7 +121,7 @@ { return type == Type::string || type == Type::usym || type == Type::inet || type == Type::buffer || type == Type::timestamp || - type == Type::mac_address; + type == Type::mac_address || type == Type::cgroup_path; } bool SizedType::IsAggregate() const @@ -183,6 +183,7 @@ case Type::tuple: return "tuple"; break; case Type::timestamp:return "timestamp";break; case Type::mac_address: return "mac_address"; break; + case Type::cgroup_path: return "cgroup_path"; break; // clang-format on } @@ -469,6 +470,11 @@ return st; } +SizedType CreateCgroupPath() +{ + return SizedType(Type::cgroup_path, 16); +} + bool SizedType::IsSigned(void) const { return is_signed_; @@ -540,6 +546,29 @@ return inner_struct_; } +// Checks if values of this type can be copied into values of another type +// Currently checks if strings in the other type (at corresponding places) are +// larger. +bool SizedType::FitsInto(const SizedType &t) const +{ + if (IsStringTy() && t.IsStringTy()) + return GetSize() <= t.GetSize(); + + if (IsTupleTy() && t.IsTupleTy()) + { + if (GetFieldCount() != t.GetFieldCount()) + return false; + + for (ssize_t i = 0; i < GetFieldCount(); i++) + { + if (!GetField(i).type.FitsInto(t.GetField(i).type)) + return false; + } + return true; + } + return IsEqual(t); +} + } // namespace bpftrace namespace std { @@ -592,6 +621,7 @@ case bpftrace::Type::buffer: case bpftrace::Type::timestamp: case bpftrace::Type::mac_address: + case bpftrace::Type::cgroup_path: break; } diff -Nru bpftrace-0.14.1/src/types.h bpftrace-0.15.0/src/types.h --- bpftrace-0.14.1/src/types.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/types.h 2022-05-24 10:41:38.000000000 +0000 @@ -47,7 +47,8 @@ buffer, tuple, timestamp, - mac_address + mac_address, + cgroup_path // clang-format on }; @@ -85,7 +86,6 @@ } }; -class BPFtrace; struct Struct; struct Field; @@ -195,6 +195,7 @@ bool operator==(const SizedType &t) const; bool operator!=(const SizedType &t) const; bool IsSameType(const SizedType &t) const; + bool FitsInto(const SizedType &t) const; bool IsPrintableTy() { @@ -362,6 +363,10 @@ { return type == Type::mac_address; }; + bool IsCgroupPathTy(void) const + { + return type == Type::cgroup_path; + }; friend std::ostream &operator<<(std::ostream &, const SizedType &); friend std::ostream &operator<<(std::ostream &, Type); @@ -420,6 +425,7 @@ SizedType CreateBuffer(size_t size); SizedType CreateTimestamp(); SizedType CreateMacAddress(); +SizedType CreateCgroupPath(); std::ostream &operator<<(std::ostream &os, const SizedType &type); @@ -487,6 +493,7 @@ std::string orig_name; // original full probe name, // before wildcard expansion std::string name; // full probe name + bool need_expansion; std::string pin; // pin file for iterator probes std::string ns; // for USDT probes, if provider namespace not from path uint64_t loc = 0; // for USDT probes diff -Nru bpftrace-0.14.1/src/utils.cpp bpftrace-0.15.0/src/utils.cpp --- bpftrace-0.14.1/src/utils.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/utils.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -115,30 +117,64 @@ void StdioSilencer::silence() { - fflush(ofile); - int fd = fileno(ofile); - assert(fd >= 0); - old_stdio_ = dup(fd); - assert(old_stdio_ >= 0); - int new_stdio_ = open("/dev/null", O_WRONLY); - assert(new_stdio_ >= 0); - [[maybe_unused]] int ret = dup2(new_stdio_, fd); - assert(ret >= 0); - close(new_stdio_); + auto syserr = [](std::string msg) { + return std::system_error(errno, std::generic_category(), msg); + }; + + try + { + int fd = fileno(ofile); + if (fd < 0) + throw syserr("fileno()"); + + fflush(ofile); + + if ((old_stdio_ = dup(fd)) < 0) + throw syserr("dup(fd)"); + + int new_stdio = -1; + if ((new_stdio = open("/dev/null", O_WRONLY)) < 0) + throw syserr("open(\"/dev/null\")"); + + if (dup2(new_stdio, fd) < 0) + throw syserr("dup2(new_stdio_, fd)"); + + close(new_stdio); + } + catch (const std::system_error &e) + { + if (errno == EMFILE) + LOG(FATAL) << e.what() << ": please raise NOFILE"; + else + LOG(BUG) << e.what(); + } } StdioSilencer::~StdioSilencer() { - if (old_stdio_ != -1) + if (old_stdio_ == -1) + return; + + auto syserr = [](std::string msg) { + return std::system_error(errno, std::generic_category(), msg); + }; + + try { - fflush(ofile); int fd = fileno(ofile); - assert(fd >= 0); - [[maybe_unused]] int ret = dup2(old_stdio_, fd); - assert(ret >= 0); + if (fd < 0) + throw syserr("fileno()"); + + fflush(ofile); + if (dup2(old_stdio_, fd) < 0) + throw syserr("dup2(old_stdio_)"); close(old_stdio_); old_stdio_ = -1; } + catch (const std::system_error &e) + { + LOG(BUG) << e.what(); + } } bool get_uint64_env_var(const std::string &str, uint64_t &dest) @@ -228,6 +264,25 @@ return true; } +/* + * Splits input string by '*' delimiter and return the individual parts. + * Sets start_wildcard and end_wildcard if input starts or ends with '*'. + */ +std::vector get_wildcard_tokens(const std::string &input, + bool &start_wildcard, + bool &end_wildcard) +{ + if (input.empty()) + return {}; + + start_wildcard = input[0] == '*'; + end_wildcard = input[input.length() - 1] == '*'; + + std::vector tokens = split_string(input, '*'); + tokens.erase(std::remove(tokens.begin(), tokens.end(), ""), tokens.end()); + return tokens; +} + std::vector get_online_cpus() { return read_cpu_range("/sys/devices/system/cpu/online"); @@ -304,6 +359,123 @@ return cflags; } +std::string get_cgroup_path_in_hierarchy(uint64_t cgroupid, + std::string base_path) +{ + static std::map, std::string> path_cache; + struct stat path_st; + + auto cached_path = path_cache.find({ cgroupid, base_path }); + if (cached_path != path_cache.end() && + stat(cached_path->second.c_str(), &path_st) >= 0 && + path_st.st_ino == cgroupid) + return cached_path->second; + + // Check for root cgroup path separately, since recursive_directory_iterator + // does not iterate over base directory + if (stat(base_path.c_str(), &path_st) >= 0 && path_st.st_ino == cgroupid) + { + path_cache[{ cgroupid, base_path }] = "/"; + return "/"; + } + + for (auto &path_iter : + std_filesystem::recursive_directory_iterator(base_path)) + { + if (stat(path_iter.path().c_str(), &path_st) < 0) + return ""; + if (path_st.st_ino == cgroupid) + { + // Base directory is not a part of cgroup path + path_cache[{ cgroupid, base_path }] = path_iter.path().string().substr( + base_path.length()); + return path_cache[{ cgroupid, base_path }]; + } + } + + return ""; +} + +std::vector> get_cgroup_hierarchy_roots() +{ + // Get all cgroup mounts and their type (cgroup/cgroup2) from /proc/mounts + std::ifstream mounts_file("/proc/mounts"); + std::vector> result; + + const std::regex cgroup_mount_regex("(cgroup[2]?) (\\S*)[ ]?.*"); + for (std::string line; std::getline(mounts_file, line);) + { + std::smatch match; + if (std::regex_match(line, match, cgroup_mount_regex)) + { + result.push_back({ match[1].str(), match[2].str() }); + } + } + + mounts_file.close(); + return result; +} + +std::vector> get_cgroup_paths( + uint64_t cgroupid, + std::string filter) +{ + // TODO: Rewrite using std::views when C++20 support becomes common + auto roots = get_cgroup_hierarchy_roots(); + + // Replace cgroup version with cgroup mount point directory name for cgroupv1 + // roots and "unified" for cgroupv2 roots + for (auto &root : roots) + { + if (root.first == "cgroup") + { + root = { std_filesystem::path(root.second).filename().string(), + root.second }; + } + else if (root.first == "cgroup2") + { + root = { "unified", root.second }; + } + } + + // Filter roots + bool start_wildcard, end_wildcard; + auto tokens = get_wildcard_tokens(filter, start_wildcard, end_wildcard); + std::vector> filtered_roots; + std::copy_if(roots.begin(), + roots.end(), + std::back_inserter(filtered_roots), + [&tokens, &start_wildcard, &end_wildcard](auto &pair) { + return wildcard_match( + pair.first, tokens, start_wildcard, end_wildcard); + }); + + // Get cgroup path for each root + std::vector> result; + std::transform(filtered_roots.begin(), + filtered_roots.end(), + std::back_inserter(result), + [&cgroupid](auto &pair) { + return std::pair{ + pair.first, + get_cgroup_path_in_hierarchy(cgroupid, pair.second) + }; + }); + + // Sort paths lexically by name (with the exception of unified, which always + // comes first) + std::sort(result.begin(), result.end(), [](auto &pair1, auto &pair2) { + if (pair1.first == "unified") + return true; + else if (pair2.first == "unified") + return false; + else + return pair1.first < pair2.first; + }); + + return result; +} + bool is_dir(const std::string& path) { std::error_code ec; @@ -415,7 +587,14 @@ const char *kpath_env = ::getenv("BPFTRACE_KERNEL_SOURCE"); if (kpath_env) - return std::make_tuple(kpath_env, kpath_env); + { + const char *kpath_build_env = ::getenv("BPFTRACE_KERNEL_BUILD"); + if (!kpath_build_env) + { + kpath_build_env = kpath_env; + } + return std::make_tuple(kpath_env, kpath_build_env); + } std::string kdir = std::string("/lib/modules/") + utsname.release; auto ksrc = kdir + "/source"; @@ -746,14 +925,14 @@ } } -std::string hex_format_buffer(const char *buf, size_t size) +std::string hex_format_buffer(const char *buf, size_t size, bool keep_ascii) { // Allow enough space for every byte to be sanitized in the form "\x00" char s[size * 4 + 1]; size_t offset = 0; for (size_t i = 0; i < size; i++) - if (buf[i] >= 32 && buf[i] <= 126) + if (keep_ascii && buf[i] >= 32 && buf[i] <= 126) offset += sprintf(s + offset, "%c", ((const uint8_t *)buf)[i]); else offset += sprintf(s + offset, "\\x%02x", ((const uint8_t *)buf)[i]); @@ -907,9 +1086,10 @@ return *a2; } default: - throw std::runtime_error("BUG: kernel_version(): Invalid attempt: " + - std::to_string(attempt)); + LOG(BUG) << "kernel_version(): Invalid attempt: " + << std::to_string(attempt); } + // lgtm[cpp/missing-return] } std::optional abs_path(const std::string &rel_path) diff -Nru bpftrace-0.14.1/src/utils.h bpftrace-0.15.0/src/utils.h --- bpftrace-0.14.1/src/utils.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/src/utils.h 2022-05-24 10:41:38.000000000 +0000 @@ -149,6 +149,9 @@ std::vector &tokens, bool start_wildcard, bool end_wildcard); +std::vector get_wildcard_tokens(const std::string &input, + bool &start_wildcard, + bool &end_wildcard); std::vector get_online_cpus(); std::vector get_possible_cpus(); bool is_dir(const std::string &path); @@ -158,6 +161,12 @@ std::vector get_kernel_cflags(const char *uname_machine, const std::string &ksrc, const std::string &kobj); +std::string get_cgroup_path_in_hierarchy(uint64_t cgroupid, + std::string base_path); +std::vector> get_cgroup_hierarchy_roots(); +std::vector> get_cgroup_paths( + uint64_t cgroupid, + std::string filter); std::unordered_set get_traceable_funcs(); const std::string &is_deprecated(const std::string &str); bool is_unsafe_func(const std::string &func_name); @@ -172,7 +181,9 @@ bool is_numeric(const std::string &str); bool symbol_has_cpp_mangled_signature(const std::string &sym_name); pid_t parse_pid(const std::string &str); -std::string hex_format_buffer(const char *buf, size_t size); +std::string hex_format_buffer(const char *buf, + size_t size, + bool keep_ascii = true); std::optional abs_path(const std::string &rel_path); bool symbol_has_module(const std::string &symbol); std::string strip_symbol_module(const std::string &symbol); diff -Nru bpftrace-0.14.1/tests/bpftrace.cpp bpftrace-0.15.0/tests/bpftrace.cpp --- bpftrace-0.14.1/tests/bpftrace.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/bpftrace.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -63,8 +63,8 @@ { throw std::runtime_error("Parser failed"); } - auto probe = d.root_->probes->front(); - d.root_->probes->clear(); + auto probe = d.root->probes->front(); + d.root->probes->clear(); return probe; } @@ -727,6 +727,17 @@ ASSERT_EQ(0, bpftrace.add_probe(*probe)); } +TEST(bpftrace, empty_attachpoints) +{ + StrictMock bpftrace; + Driver driver(bpftrace); + + // Trailing comma is fine + ASSERT_EQ(driver.parse_str("kprobe:f1, {}"), 0); + // Empty attach point should fail + ASSERT_EQ(driver.parse_str("{}"), 1); +} + std::pair, std::vector> key_value_pair_int(std::vector key, int val) { std::pair, std::vector> pair; diff -Nru bpftrace-0.14.1/tests/clang_parser.cpp bpftrace-0.15.0/tests/clang_parser.cpp --- bpftrace-0.14.1/tests/clang_parser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/clang_parser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -17,11 +17,11 @@ Driver driver(bpftrace); ASSERT_EQ(driver.parse_str(extended_input), 0); - ast::FieldAnalyser fields(driver.root_, bpftrace); + ast::FieldAnalyser fields(driver.root.get(), bpftrace); EXPECT_EQ(fields.analyse(), 0); ClangParser clang; - ASSERT_EQ(clang.parse(driver.root_, bpftrace), result); + ASSERT_EQ(clang.parse(driver.root.get(), bpftrace), result); } TEST(clang_parser, integers) diff -Nru bpftrace-0.14.1/tests/codegen/call_cgroup_path.cpp bpftrace-0.15.0/tests/codegen/call_cgroup_path.cpp --- bpftrace-0.14.1/tests/codegen/call_cgroup_path.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/call_cgroup_path.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,14 @@ +#include "common.h" + +namespace bpftrace { +namespace test { +namespace codegen { + +TEST(codegen, call_cgroup_path) +{ + test("kprobe:f { print(cgroup_path(cgroup)); }", NAME); +} + +} // namespace codegen +} // namespace test +} // namespace bpftrace diff -Nru bpftrace-0.14.1/tests/codegen/call_kstack.cpp bpftrace-0.15.0/tests/codegen/call_kstack.cpp --- bpftrace-0.14.1/tests/codegen/call_kstack.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/call_kstack.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -27,19 +27,19 @@ 0); ClangParser clang; - clang.parse(driver.root_, *bpftrace); + clang.parse(driver.root.get(), *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, *bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), *bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::ResourceAnalyser resource_analyser(driver.root_); + ast::ResourceAnalyser resource_analyser(driver.root.get()); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; - ast::CodegenLLVM codegen(driver.root_, *bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), *bpftrace); codegen.compile(); ASSERT_EQ(std::distance(bpftrace->maps.begin(), bpftrace->maps.end()), 6); @@ -62,19 +62,19 @@ 0); ClangParser clang; - clang.parse(driver.root_, *bpftrace); + clang.parse(driver.root.get(), *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, *bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), *bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::ResourceAnalyser resource_analyser(driver.root_); + ast::ResourceAnalyser resource_analyser(driver.root.get()); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; - ast::CodegenLLVM codegen(driver.root_, *bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), *bpftrace); codegen.compile(); ASSERT_EQ(std::distance(bpftrace->maps.begin(), bpftrace->maps.end()), 6); diff -Nru bpftrace-0.14.1/tests/codegen/call_ustack.cpp bpftrace-0.15.0/tests/codegen/call_ustack.cpp --- bpftrace-0.14.1/tests/codegen/call_ustack.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/call_ustack.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -27,19 +27,19 @@ 0); ClangParser clang; - clang.parse(driver.root_, *bpftrace); + clang.parse(driver.root.get(), *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, *bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), *bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::ResourceAnalyser resource_analyser(driver.root_); + ast::ResourceAnalyser resource_analyser(driver.root.get()); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; - ast::CodegenLLVM codegen(driver.root_, *bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), *bpftrace); codegen.compile(); ASSERT_EQ(std::distance(bpftrace->maps.begin(), bpftrace->maps.end()), 6); @@ -62,19 +62,19 @@ 0); ClangParser clang; - clang.parse(driver.root_, *bpftrace); + clang.parse(driver.root.get(), *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, *bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), *bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::ResourceAnalyser resource_analyser(driver.root_); + ast::ResourceAnalyser resource_analyser(driver.root.get()); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; - ast::CodegenLLVM codegen(driver.root_, *bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), *bpftrace); codegen.compile(); ASSERT_EQ(std::distance(bpftrace->maps.begin(), bpftrace->maps.end()), 6); diff -Nru bpftrace-0.14.1/tests/codegen/common.h bpftrace-0.15.0/tests/codegen/common.h --- bpftrace-0.14.1/tests/codegen/common.h 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/common.h 2022-05-24 10:41:38.000000000 +0000 @@ -46,22 +46,22 @@ ASSERT_EQ(driver.parse_str(input), 0); ClangParser clang; - clang.parse(driver.root_, bpftrace); + clang.parse(driver.root.get(), bpftrace); ASSERT_EQ(driver.parse_str(input), 0); // Override to mockbpffeature. bpftrace.feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::ResourceAnalyser resource_analyser(driver.root_); + ast::ResourceAnalyser resource_analyser(driver.root.get()); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(bpftrace, true), 0); bpftrace.resources = resources; std::stringstream out; - ast::CodegenLLVM codegen(driver.root_, bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), bpftrace); codegen.generate_ir(); codegen.DumpIR(out); // Test that generated code compiles cleanly diff -Nru bpftrace-0.14.1/tests/codegen/general.cpp bpftrace-0.15.0/tests/codegen/general.cpp --- bpftrace-0.14.1/tests/codegen/general.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/general.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -45,9 +45,9 @@ ASSERT_EQ(driver.parse_str("kprobe:foo { 1 } kprobe:bar { 1 }"), 0); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, *bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), *bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::CodegenLLVM codegen(driver.root_, *bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), *bpftrace); auto bpforc = codegen.compile(); EXPECT_TRUE(bpforc->getSection("s_kprobe:foo_1").has_value()); @@ -68,23 +68,23 @@ "}"), 0); ClangParser clang; - clang.parse(driver.root_, *bpftrace); + clang.parse(driver.root.get(), *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, *bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), *bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::ResourceAnalyser resource_analyser(driver.root_); + ast::ResourceAnalyser resource_analyser(driver.root.get()); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; - ast::CodegenLLVM codegen(driver.root_, *bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), *bpftrace); codegen.generate_ir(); EXPECT_EQ(resources.printf_args.size(), 1U); - auto &fmt = std::get<0>(bpftrace->resources.printf_args[0]); + auto fmt = std::get<0>(bpftrace->resources.printf_args[0]).str(); auto &args = std::get<1>(bpftrace->resources.printf_args[0]); EXPECT_EQ(fmt, "%c %u %s %p\n"); @@ -120,9 +120,9 @@ ASSERT_EQ(driver.parse_str("kprobe:f { 1; } kprobe:d { 1; }"), 0); // Override to mockbpffeature. bpftrace.feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::CodegenLLVM codegen(driver.root_, bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), bpftrace); codegen.generate_ir(); } } // namespace codegen diff -Nru bpftrace-0.14.1/tests/codegen/llvm/builtin_ctx_field.ll bpftrace-0.15.0/tests/codegen/llvm/builtin_ctx_field.ll --- bpftrace-0.14.1/tests/codegen/llvm/builtin_ctx_field.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/builtin_ctx_field.ll 2022-05-24 10:41:38.000000000 +0000 @@ -85,14 +85,14 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %"struct c.c") %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %"struct c.c", i32 1, i64 %35) %36 = load i8, i8* %"struct c.c", align 1 - %37 = sext i8 %36 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %"struct c.c") - %38 = bitcast i64* %"@d_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %38) + %37 = bitcast i64* %"@d_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %37) store i64 0, i64* %"@d_key", align 8 + %38 = sext i8 %36 to i64 %39 = bitcast i64* %"@d_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %39) - store i64 %37, i64* %"@d_val", align 8 + store i64 %38, i64* %"@d_val", align 8 %pseudo5 = call i64 @llvm.bpf.pseudo(i64 1, i64 3) %update_elem6 = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo5, i64* %"@d_key", i64* %"@d_val", i64 0) %40 = bitcast i64* %"@d_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/call_cgroup_path.ll bpftrace-0.15.0/tests/codegen/llvm/call_cgroup_path.ll --- bpftrace-0.14.1/tests/codegen/llvm/call_cgroup_path.ll 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/call_cgroup_path.ll 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,56 @@ +; ModuleID = 'bpftrace' +source_filename = "bpftrace" +target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" +target triple = "bpf-pc-linux" + +%print_cgroup_path_16_t = type <{ i64, i64, [16 x i8] }> +%cgroup_path_t = type <{ i64, i64 }> + +; Function Attrs: nounwind +declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 + +define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { +entry: + %print_cgroup_path_16_t = alloca %print_cgroup_path_16_t, align 8 + %cgroup_path_args = alloca %cgroup_path_t, align 8 + %1 = bitcast %cgroup_path_t* %cgroup_path_args to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) + %2 = getelementptr %cgroup_path_t, %cgroup_path_t* %cgroup_path_args, i64 0, i32 0 + store i64 0, i64* %2, align 8 + %get_cgroup_id = call i64 inttoptr (i64 80 to i64 ()*)() + %3 = getelementptr %cgroup_path_t, %cgroup_path_t* %cgroup_path_args, i64 0, i32 1 + store i64 %get_cgroup_id, i64* %3, align 8 + %4 = bitcast %print_cgroup_path_16_t* %print_cgroup_path_16_t to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) + %5 = getelementptr %print_cgroup_path_16_t, %print_cgroup_path_16_t* %print_cgroup_path_16_t, i64 0, i32 0 + store i64 30007, i64* %5, align 8 + %6 = getelementptr %print_cgroup_path_16_t, %print_cgroup_path_16_t* %print_cgroup_path_16_t, i64 0, i32 1 + store i64 0, i64* %6, align 8 + %7 = getelementptr %print_cgroup_path_16_t, %print_cgroup_path_16_t* %print_cgroup_path_16_t, i32 0, i32 2 + %8 = bitcast [16 x i8]* %7 to i8* + call void @llvm.memset.p0i8.i64(i8* align 1 %8, i8 0, i64 16, i1 false) + %9 = bitcast [16 x i8]* %7 to i8* + %10 = bitcast %cgroup_path_t* %cgroup_path_args to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %9, i8* align 1 %10, i64 16, i1 false) + %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) + %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %print_cgroup_path_16_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %print_cgroup_path_16_t* %print_cgroup_path_16_t, i64 32) + %11 = bitcast %print_cgroup_path_16_t* %print_cgroup_path_16_t to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) + ret i64 0 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg %0, i8* nocapture %1) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly %0, i8* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg %0, i8* nocapture %1) #1 + +attributes #0 = { nounwind } +attributes #1 = { argmemonly nofree nosync nounwind willreturn } +attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } diff -Nru bpftrace-0.14.1/tests/codegen/llvm/call_print_composit.ll bpftrace-0.15.0/tests/codegen/llvm/call_print_composit.ll --- bpftrace-0.14.1/tests/codegen/llvm/call_print_composit.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/call_print_composit.ll 2022-05-24 10:41:38.000000000 +0000 @@ -3,49 +3,49 @@ target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" -%print_tuple_72_t = type <{ i64, i64, [72 x i8] }> -%"int64_string[64]__tuple_t" = type { i64, [64 x i8] } +%print_tuple_16_t = type <{ i64, i64, [16 x i8] }> +%"int64_string[4]__tuple_t" = type { i64, [4 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: - %print_tuple_72_t = alloca %print_tuple_72_t, align 8 - %str = alloca [64 x i8], align 1 - %tuple = alloca %"int64_string[64]__tuple_t", align 8 - %1 = bitcast %"int64_string[64]__tuple_t"* %tuple to i8* + %print_tuple_16_t = alloca %print_tuple_16_t, align 8 + %str = alloca [4 x i8], align 1 + %tuple = alloca %"int64_string[4]__tuple_t", align 8 + %1 = bitcast %"int64_string[4]__tuple_t"* %tuple to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) - %2 = bitcast %"int64_string[64]__tuple_t"* %tuple to i8* - call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 72, i1 false) - %3 = getelementptr %"int64_string[64]__tuple_t", %"int64_string[64]__tuple_t"* %tuple, i32 0, i32 0 + %2 = bitcast %"int64_string[4]__tuple_t"* %tuple to i8* + call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 16, i1 false) + %3 = getelementptr %"int64_string[4]__tuple_t", %"int64_string[4]__tuple_t"* %tuple, i32 0, i32 0 store i64 1, i64* %3, align 8 - %4 = bitcast [64 x i8]* %str to i8* + %4 = bitcast [4 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %4) - store [64 x i8] c"abc\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 - %5 = getelementptr %"int64_string[64]__tuple_t", %"int64_string[64]__tuple_t"* %tuple, i32 0, i32 1 - %6 = bitcast [64 x i8]* %5 to i8* - %7 = bitcast [64 x i8]* %str to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %6, i8* align 1 %7, i64 64, i1 false) - %8 = bitcast [64 x i8]* %str to i8* + store [4 x i8] c"abc\00", [4 x i8]* %str, align 1 + %5 = getelementptr %"int64_string[4]__tuple_t", %"int64_string[4]__tuple_t"* %tuple, i32 0, i32 1 + %6 = bitcast [4 x i8]* %5 to i8* + %7 = bitcast [4 x i8]* %str to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %6, i8* align 1 %7, i64 4, i1 false) + %8 = bitcast [4 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) - %9 = bitcast %print_tuple_72_t* %print_tuple_72_t to i8* + %9 = bitcast %print_tuple_16_t* %print_tuple_16_t to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) - %10 = getelementptr %print_tuple_72_t, %print_tuple_72_t* %print_tuple_72_t, i64 0, i32 0 + %10 = getelementptr %print_tuple_16_t, %print_tuple_16_t* %print_tuple_16_t, i64 0, i32 0 store i64 30007, i64* %10, align 8 - %11 = getelementptr %print_tuple_72_t, %print_tuple_72_t* %print_tuple_72_t, i64 0, i32 1 + %11 = getelementptr %print_tuple_16_t, %print_tuple_16_t* %print_tuple_16_t, i64 0, i32 1 store i64 0, i64* %11, align 8 - %12 = getelementptr %print_tuple_72_t, %print_tuple_72_t* %print_tuple_72_t, i32 0, i32 2 - %13 = bitcast [72 x i8]* %12 to i8* - call void @llvm.memset.p0i8.i64(i8* align 1 %13, i8 0, i64 72, i1 false) - %14 = bitcast [72 x i8]* %12 to i8* - %15 = bitcast %"int64_string[64]__tuple_t"* %tuple to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %14, i8* align 1 %15, i64 72, i1 false) + %12 = getelementptr %print_tuple_16_t, %print_tuple_16_t* %print_tuple_16_t, i32 0, i32 2 + %13 = bitcast [16 x i8]* %12 to i8* + call void @llvm.memset.p0i8.i64(i8* align 1 %13, i8 0, i64 16, i1 false) + %14 = bitcast [16 x i8]* %12 to i8* + %15 = bitcast %"int64_string[4]__tuple_t"* %tuple to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %14, i8* align 1 %15, i64 16, i1 false) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) - %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %print_tuple_72_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %print_tuple_72_t* %print_tuple_72_t, i64 88) - %16 = bitcast %print_tuple_72_t* %print_tuple_72_t to i8* + %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %print_tuple_16_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %print_tuple_16_t* %print_tuple_16_t, i64 32) + %16 = bitcast %print_tuple_16_t* %print_tuple_16_t to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16) - %17 = bitcast %"int64_string[64]__tuple_t"* %tuple to i8* + %17 = bitcast %"int64_string[4]__tuple_t"* %tuple to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %17) ret i64 0 } diff -Nru bpftrace-0.14.1/tests/codegen/llvm/call_printf.ll bpftrace-0.15.0/tests/codegen/llvm/call_printf.ll --- bpftrace-0.14.1/tests/codegen/llvm/call_printf.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/call_printf.ll 2022-05-24 10:41:38.000000000 +0000 @@ -32,10 +32,10 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %"struct Foo.c") %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %"struct Foo.c", i32 1, i64 %8) %9 = load i8, i8* %"struct Foo.c", align 1 - %10 = sext i8 %9 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %"struct Foo.c") - %11 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 - store i64 %10, i64* %11, align 8 + %10 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 + %11 = sext i8 %9 to i64 + store i64 %11, i64* %10, align 8 %12 = load i64, i64* %"$foo", align 8 %13 = add i64 %12, 8 %14 = bitcast i64* %"struct Foo.l" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/logical_and_or_different_type.ll bpftrace-0.15.0/tests/codegen/llvm/logical_and_or_different_type.ll --- bpftrace-0.14.1/tests/codegen/llvm/logical_and_or_different_type.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/logical_and_or_different_type.ll 2022-05-24 10:41:38.000000000 +0000 @@ -41,10 +41,9 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %probe_read_user = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m", i32 4, i64 %9) %11 = load i32, i32* %"struct Foo.m", align 4 - %12 = sext i32 %11 to i64 - %13 = bitcast i32* %"struct Foo.m" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) - %lhs_true_cond = icmp ne i64 %12, 0 + %12 = bitcast i32* %"struct Foo.m" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) + %lhs_true_cond = icmp ne i32 %11, 0 br i1 %lhs_true_cond, label %"&&_lhs_true", label %"&&_false" "&&_lhs_true": ; preds = %entry @@ -59,24 +58,23 @@ br label %"&&_merge" "&&_merge": ; preds = %"&&_false", %"&&_true" - %14 = load i64, i64* %"&&_result", align 8 - %15 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 - store i64 %14, i64* %15, align 8 - %16 = bitcast i64* %"&&_result5" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %16) + %13 = load i64, i64* %"&&_result", align 8 + %14 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 1 + store i64 %13, i64* %14, align 8 + %15 = bitcast i64* %"&&_result5" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %15) br i1 true, label %"&&_lhs_true1", label %"&&_false3" "&&_lhs_true1": ; preds = %"&&_merge" - %17 = load i64, i64* %"$foo", align 8 - %18 = add i64 %17, 0 - %19 = bitcast i32* %"struct Foo.m6" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %19) - %probe_read_user7 = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m6", i32 4, i64 %18) - %20 = load i32, i32* %"struct Foo.m6", align 4 - %21 = sext i32 %20 to i64 - %22 = bitcast i32* %"struct Foo.m6" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %22) - %rhs_true_cond = icmp ne i64 %21, 0 + %16 = load i64, i64* %"$foo", align 8 + %17 = add i64 %16, 0 + %18 = bitcast i32* %"struct Foo.m6" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %18) + %probe_read_user7 = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m6", i32 4, i64 %17) + %19 = load i32, i32* %"struct Foo.m6", align 4 + %20 = bitcast i32* %"struct Foo.m6" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20) + %rhs_true_cond = icmp ne i32 %19, 0 br i1 %rhs_true_cond, label %"&&_true2", label %"&&_false3" "&&_true2": ; preds = %"&&_lhs_true1" @@ -88,21 +86,20 @@ br label %"&&_merge4" "&&_merge4": ; preds = %"&&_false3", %"&&_true2" - %23 = load i64, i64* %"&&_result5", align 8 - %24 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 2 - store i64 %23, i64* %24, align 8 - %25 = bitcast i64* %"||_result" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %25) - %26 = load i64, i64* %"$foo", align 8 - %27 = add i64 %26, 0 + %21 = load i64, i64* %"&&_result5", align 8 + %22 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 2 + store i64 %21, i64* %22, align 8 + %23 = bitcast i64* %"||_result" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %23) + %24 = load i64, i64* %"$foo", align 8 + %25 = add i64 %24, 0 + %26 = bitcast i32* %"struct Foo.m8" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %26) + %probe_read_user9 = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m8", i32 4, i64 %25) + %27 = load i32, i32* %"struct Foo.m8", align 4 %28 = bitcast i32* %"struct Foo.m8" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %28) - %probe_read_user9 = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m8", i32 4, i64 %27) - %29 = load i32, i32* %"struct Foo.m8", align 4 - %30 = sext i32 %29 to i64 - %31 = bitcast i32* %"struct Foo.m8" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %31) - %lhs_true_cond10 = icmp ne i64 %30, 0 + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %28) + %lhs_true_cond10 = icmp ne i32 %27, 0 br i1 %lhs_true_cond10, label %"||_true", label %"||_lhs_false" "||_lhs_false": ; preds = %"&&_merge4" @@ -117,24 +114,23 @@ br label %"||_merge" "||_merge": ; preds = %"||_true", %"||_false" - %32 = load i64, i64* %"||_result", align 8 - %33 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 3 - store i64 %32, i64* %33, align 8 - %34 = bitcast i64* %"||_result15" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %34) + %29 = load i64, i64* %"||_result", align 8 + %30 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 3 + store i64 %29, i64* %30, align 8 + %31 = bitcast i64* %"||_result15" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %31) br i1 false, label %"||_true13", label %"||_lhs_false11" "||_lhs_false11": ; preds = %"||_merge" - %35 = load i64, i64* %"$foo", align 8 - %36 = add i64 %35, 0 - %37 = bitcast i32* %"struct Foo.m16" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %37) - %probe_read_user17 = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m16", i32 4, i64 %36) - %38 = load i32, i32* %"struct Foo.m16", align 4 - %39 = sext i32 %38 to i64 - %40 = bitcast i32* %"struct Foo.m16" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %40) - %rhs_true_cond18 = icmp ne i64 %39, 0 + %32 = load i64, i64* %"$foo", align 8 + %33 = add i64 %32, 0 + %34 = bitcast i32* %"struct Foo.m16" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %34) + %probe_read_user17 = call i64 inttoptr (i64 112 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.m16", i32 4, i64 %33) + %35 = load i32, i32* %"struct Foo.m16", align 4 + %36 = bitcast i32* %"struct Foo.m16" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %36) + %rhs_true_cond18 = icmp ne i32 %35, 0 br i1 %rhs_true_cond18, label %"||_true13", label %"||_false12" "||_false12": ; preds = %"||_lhs_false11" @@ -146,13 +142,13 @@ br label %"||_merge14" "||_merge14": ; preds = %"||_true13", %"||_false12" - %41 = load i64, i64* %"||_result15", align 8 - %42 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 4 - store i64 %41, i64* %42, align 8 + %37 = load i64, i64* %"||_result15", align 8 + %38 = getelementptr %printf_t, %printf_t* %printf_args, i32 0, i32 4 + store i64 %37, i64* %38, align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i64, i64, %printf_t*, i64)*)(i8* %0, i64 %pseudo, i64 4294967295, %printf_t* %printf_args, i64 40) - %43 = bitcast %printf_t* %printf_args to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %43) + %39 = bitcast %printf_t* %printf_args to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %39) ret i64 0 } diff -Nru bpftrace-0.14.1/tests/codegen/llvm/map_assign_string.ll bpftrace-0.15.0/tests/codegen/llvm/map_assign_string.ll --- bpftrace-0.14.1/tests/codegen/llvm/map_assign_string.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/map_assign_string.ll 2022-05-24 10:41:38.000000000 +0000 @@ -9,18 +9,18 @@ define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 - %str = alloca [64 x i8], align 1 - %1 = bitcast [64 x i8]* %str to i8* + %str = alloca [5 x i8], align 1 + %1 = bitcast [5 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) - store [64 x i8] c"blah\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 + store [5 x i8] c"blah\00", [5 x i8]* %str, align 1 %2 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) - %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [64 x i8]* %str, i64 0) + %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [5 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [5 x i8]* %str, i64 0) %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) - %4 = bitcast [64 x i8]* %str to i8* + %4 = bitcast [5 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) ret i64 0 } diff -Nru bpftrace-0.14.1/tests/codegen/llvm/map_key_string.ll bpftrace-0.15.0/tests/codegen/llvm/map_key_string.ll --- bpftrace-0.14.1/tests/codegen/llvm/map_key_string.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/map_key_string.ll 2022-05-24 10:41:38.000000000 +0000 @@ -9,35 +9,35 @@ define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_val" = alloca i64, align 8 - %str1 = alloca [64 x i8], align 1 - %str = alloca [64 x i8], align 1 - %"@x_key" = alloca [128 x i8], align 1 - %1 = bitcast [128 x i8]* %"@x_key" to i8* + %str1 = alloca [2 x i8], align 1 + %str = alloca [2 x i8], align 1 + %"@x_key" = alloca [4 x i8], align 1 + %1 = bitcast [4 x i8]* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) - %2 = bitcast [64 x i8]* %str to i8* + %2 = bitcast [2 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) - store [64 x i8] c"a\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 - %3 = getelementptr [128 x i8], [128 x i8]* %"@x_key", i64 0, i64 0 - %4 = bitcast [64 x i8]* %str to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %3, i8* align 1 %4, i64 64, i1 false) - %5 = bitcast [64 x i8]* %str to i8* + store [2 x i8] c"a\00", [2 x i8]* %str, align 1 + %3 = getelementptr [4 x i8], [4 x i8]* %"@x_key", i64 0, i64 0 + %4 = bitcast [2 x i8]* %str to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %3, i8* align 1 %4, i64 2, i1 false) + %5 = bitcast [2 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %5) - %6 = bitcast [64 x i8]* %str1 to i8* + %6 = bitcast [2 x i8]* %str1 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) - store [64 x i8] c"b\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str1, align 1 - %7 = getelementptr [128 x i8], [128 x i8]* %"@x_key", i64 0, i64 64 - %8 = bitcast [64 x i8]* %str1 to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %8, i64 64, i1 false) - %9 = bitcast [64 x i8]* %str1 to i8* + store [2 x i8] c"b\00", [2 x i8]* %str1, align 1 + %7 = getelementptr [4 x i8], [4 x i8]* %"@x_key", i64 0, i64 2 + %8 = bitcast [2 x i8]* %str1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %8, i64 2, i1 false) + %9 = bitcast [2 x i8]* %str1 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 44, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) - %update_elem = call i64 inttoptr (i64 2 to i64 (i64, [128 x i8]*, i64*, i64)*)(i64 %pseudo, [128 x i8]* %"@x_key", i64* %"@x_val", i64 0) + %update_elem = call i64 inttoptr (i64 2 to i64 (i64, [4 x i8]*, i64*, i64)*)(i64 %pseudo, [4 x i8]* %"@x_key", i64* %"@x_val", i64 0) %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) - %12 = bitcast [128 x i8]* %"@x_key" to i8* + %12 = bitcast [4 x i8]* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } diff -Nru bpftrace-0.14.1/tests/codegen/llvm/string_propagation.ll bpftrace-0.15.0/tests/codegen/llvm/string_propagation.ll --- bpftrace-0.14.1/tests/codegen/llvm/string_propagation.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/string_propagation.ll 2022-05-24 10:41:38.000000000 +0000 @@ -9,40 +9,40 @@ define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@y_key" = alloca i64, align 8 - %lookup_elem_val = alloca [64 x i8], align 1 + %lookup_elem_val = alloca [5 x i8], align 1 %"@x_key1" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 - %str = alloca [64 x i8], align 1 - %1 = bitcast [64 x i8]* %str to i8* + %str = alloca [5 x i8], align 1 + %1 = bitcast [5 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) - store [64 x i8] c"asdf\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 + store [5 x i8] c"asdf\00", [5 x i8]* %str, align 1 %2 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2) store i64 0, i64* %"@x_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) - %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [64 x i8]* %str, i64 0) + %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, [5 x i8]*, i64)*)(i64 %pseudo, i64* %"@x_key", [5 x i8]* %str, i64 0) %3 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %3) - %4 = bitcast [64 x i8]* %str to i8* + %4 = bitcast [5 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %4) %5 = bitcast i64* %"@x_key1" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) store i64 0, i64* %"@x_key1", align 8 %pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %lookup_elem = call i8* inttoptr (i64 1 to i8* (i64, i64*)*)(i64 %pseudo2, i64* %"@x_key1") - %6 = bitcast [64 x i8]* %lookup_elem_val to i8* + %6 = bitcast [5 x i8]* %lookup_elem_val to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %map_lookup_cond = icmp ne i8* %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry - %7 = bitcast [64 x i8]* %lookup_elem_val to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %lookup_elem, i64 64, i1 false) + %7 = bitcast [5 x i8]* %lookup_elem_val to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %lookup_elem, i64 5, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry - %8 = bitcast [64 x i8]* %lookup_elem_val to i8* - call void @llvm.memset.p0i8.i64(i8* align 1 %8, i8 0, i64 64, i1 false) + %8 = bitcast [5 x i8]* %lookup_elem_val to i8* + call void @llvm.memset.p0i8.i64(i8* align 1 %8, i8 0, i64 5, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success @@ -52,10 +52,10 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@y_key", align 8 %pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 1) - %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, [64 x i8]*, i64)*)(i64 %pseudo3, i64* %"@y_key", [64 x i8]* %lookup_elem_val, i64 0) + %update_elem4 = call i64 inttoptr (i64 2 to i64 (i64, i64*, [5 x i8]*, i64)*)(i64 %pseudo3, i64* %"@y_key", [5 x i8]* %lookup_elem_val, i64 0) %11 = bitcast i64* %"@y_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) - %12 = bitcast [64 x i8]* %lookup_elem_val to i8* + %12 = bitcast [5 x i8]* %lookup_elem_val to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_char_1.ll bpftrace-0.15.0/tests/codegen/llvm/struct_char_1.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_char_1.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_char_1.ll 2022-05-24 10:41:38.000000000 +0000 @@ -24,14 +24,14 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %"struct Foo.x", i32 1, i64 %5) %6 = load i8, i8* %"struct Foo.x", align 1 - %7 = sext i8 %6 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %"struct Foo.x") - %8 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) + %7 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@x_key", align 8 + %8 = sext i8 %6 to i64 %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) - store i64 %7, i64* %"@x_val", align 8 + store i64 %8, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %10 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_char_2.ll bpftrace-0.15.0/tests/codegen/llvm/struct_char_2.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_char_2.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_char_2.ll 2022-05-24 10:41:38.000000000 +0000 @@ -24,14 +24,14 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i8*, i32, i64)*)(i8* %"struct Foo.x", i32 1, i64 %5) %6 = load i8, i8* %"struct Foo.x", align 1 - %7 = sext i8 %6 to i64 call void @llvm.lifetime.end.p0i8(i64 -1, i8* %"struct Foo.x") - %8 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8) + %7 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) store i64 0, i64* %"@x_key", align 8 + %8 = sext i8 %6 to i64 %9 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) - store i64 %7, i64* %"@x_val", align 8 + store i64 %8, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %10 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_integers_1.ll bpftrace-0.15.0/tests/codegen/llvm/struct_integers_1.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_integers_1.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_integers_1.ll 2022-05-24 10:41:38.000000000 +0000 @@ -25,15 +25,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.x", i32 4, i64 %5) %7 = load i32, i32* %"struct Foo.x", align 4 - %8 = sext i32 %7 to i64 - %9 = bitcast i32* %"struct Foo.x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) - %10 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) + %8 = bitcast i32* %"struct Foo.x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) + %9 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@x_key", align 8 + %10 = sext i32 %7 to i64 %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) - store i64 %8, i64* %"@x_val", align 8 + store i64 %10, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %12 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_integers_2.ll bpftrace-0.15.0/tests/codegen/llvm/struct_integers_2.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_integers_2.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_integers_2.ll 2022-05-24 10:41:38.000000000 +0000 @@ -25,15 +25,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Foo.x", i32 4, i64 %5) %7 = load i32, i32* %"struct Foo.x", align 4 - %8 = sext i32 %7 to i64 - %9 = bitcast i32* %"struct Foo.x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) - %10 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) + %8 = bitcast i32* %"struct Foo.x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) + %9 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@x_key", align 8 + %10 = sext i32 %7 to i64 %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) - store i64 %8, i64* %"@x_val", align 8 + store i64 %10, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %12 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_anon_1.ll bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_anon_1.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_anon_1.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_anon_1.ll 2022-05-24 10:41:38.000000000 +0000 @@ -26,15 +26,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Foo::(anonymous at definitions.h:2:14).x", i32 4, i64 %6) %8 = load i32, i32* %"struct Foo::(anonymous at definitions.h:2:14).x", align 4 - %9 = sext i32 %8 to i64 - %10 = bitcast i32* %"struct Foo::(anonymous at definitions.h:2:14).x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) - %11 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) + %9 = bitcast i32* %"struct Foo::(anonymous at definitions.h:2:14).x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) + %10 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 + %11 = sext i32 %8 to i64 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) - store i64 %9, i64* %"@x_val", align 8 + store i64 %11, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_anon_2.ll bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_anon_2.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_anon_2.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_anon_2.ll 2022-05-24 10:41:38.000000000 +0000 @@ -26,15 +26,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Foo::(anonymous at definitions.h:2:14).x", i32 4, i64 %6) %8 = load i32, i32* %"struct Foo::(anonymous at definitions.h:2:14).x", align 4 - %9 = sext i32 %8 to i64 - %10 = bitcast i32* %"struct Foo::(anonymous at definitions.h:2:14).x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) - %11 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) + %9 = bitcast i32* %"struct Foo::(anonymous at definitions.h:2:14).x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) + %10 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 + %11 = sext i32 %8 to i64 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) - store i64 %9, i64* %"@x_val", align 8 + store i64 %11, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_named_1.ll bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_named_1.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_named_1.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_named_1.ll 2022-05-24 10:41:38.000000000 +0000 @@ -26,15 +26,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Bar.x", i32 4, i64 %6) %8 = load i32, i32* %"struct Bar.x", align 4 - %9 = sext i32 %8 to i64 - %10 = bitcast i32* %"struct Bar.x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) - %11 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) + %9 = bitcast i32* %"struct Bar.x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) + %10 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 + %11 = sext i32 %8 to i64 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) - store i64 %9, i64* %"@x_val", align 8 + store i64 %11, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_named_2.ll bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_named_2.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_named_2.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_named_2.ll 2022-05-24 10:41:38.000000000 +0000 @@ -26,15 +26,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Bar.x", i32 4, i64 %6) %8 = load i32, i32* %"struct Bar.x", align 4 - %9 = sext i32 %8 to i64 - %10 = bitcast i32* %"struct Bar.x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) - %11 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) + %9 = bitcast i32* %"struct Bar.x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) + %10 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 + %11 = sext i32 %8 to i64 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) - store i64 %9, i64* %"@x_val", align 8 + store i64 %11, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_ptr_named_1.ll bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_ptr_named_1.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_ptr_named_1.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_ptr_named_1.ll 2022-05-24 10:41:38.000000000 +0000 @@ -33,15 +33,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %probe_read_kernel1 = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Bar.x", i32 4, i64 %9) %11 = load i32, i32* %"struct Bar.x", align 4 - %12 = sext i32 %11 to i64 - %13 = bitcast i32* %"struct Bar.x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) - %14 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) + %12 = bitcast i32* %"struct Bar.x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) + %13 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %13) store i64 0, i64* %"@x_key", align 8 + %14 = sext i32 %11 to i64 %15 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %15) - store i64 %12, i64* %"@x_val", align 8 + store i64 %14, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %16 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_ptr_named_2.ll bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_ptr_named_2.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_nested_struct_ptr_named_2.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_nested_struct_ptr_named_2.ll 2022-05-24 10:41:38.000000000 +0000 @@ -33,15 +33,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) %probe_read_kernel1 = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %"struct Bar.x", i32 4, i64 %9) %11 = load i32, i32* %"struct Bar.x", align 4 - %12 = sext i32 %11 to i64 - %13 = bitcast i32* %"struct Bar.x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) - %14 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) + %12 = bitcast i32* %"struct Bar.x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) + %13 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %13) store i64 0, i64* %"@x_key", align 8 + %14 = sext i32 %11 to i64 %15 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %15) - store i64 %12, i64* %"@x_val", align 8 + store i64 %14, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %16 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_short_1.ll bpftrace-0.15.0/tests/codegen/llvm/struct_short_1.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_short_1.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_short_1.ll 2022-05-24 10:41:38.000000000 +0000 @@ -25,15 +25,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i16*, i32, i64)*)(i16* %"struct Foo.x", i32 2, i64 %5) %7 = load i16, i16* %"struct Foo.x", align 2 - %8 = sext i16 %7 to i64 - %9 = bitcast i16* %"struct Foo.x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) - %10 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) + %8 = bitcast i16* %"struct Foo.x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) + %9 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@x_key", align 8 + %10 = sext i16 %7 to i64 %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) - store i64 %8, i64* %"@x_val", align 8 + store i64 %10, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %12 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/struct_short_2.ll bpftrace-0.15.0/tests/codegen/llvm/struct_short_2.ll --- bpftrace-0.14.1/tests/codegen/llvm/struct_short_2.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/struct_short_2.ll 2022-05-24 10:41:38.000000000 +0000 @@ -25,15 +25,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i16*, i32, i64)*)(i16* %"struct Foo.x", i32 2, i64 %5) %7 = load i16, i16* %"struct Foo.x", align 2 - %8 = sext i16 %7 to i64 - %9 = bitcast i16* %"struct Foo.x" to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) - %10 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) + %8 = bitcast i16* %"struct Foo.x" to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %8) + %9 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) store i64 0, i64* %"@x_key", align 8 + %10 = sext i16 %7 to i64 %11 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) - store i64 %8, i64* %"@x_val", align 8 + store i64 %10, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %12 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/llvm/ternary_str.ll bpftrace-0.15.0/tests/codegen/llvm/ternary_str.ll --- bpftrace-0.14.1/tests/codegen/llvm/ternary_str.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/ternary_str.ll 2022-05-24 10:41:38.000000000 +0000 @@ -9,8 +9,8 @@ define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@x_key" = alloca i64, align 8 - %str1 = alloca [64 x i8], align 1 - %str = alloca [64 x i8], align 1 + %str1 = alloca [3 x i8], align 1 + %str = alloca [3 x i8], align 1 %buf = alloca [64 x i8], align 1 %result = alloca [64 x i8], align 1 %1 = bitcast [64 x i8]* %result to i8* @@ -25,27 +25,27 @@ br i1 %true_cond, label %left, label %right left: ; preds = %entry - %6 = bitcast [64 x i8]* %str to i8* + %6 = bitcast [3 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6) - store [64 x i8] c"lo\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 + store [3 x i8] c"lo\00", [3 x i8]* %str, align 1 %7 = bitcast [64 x i8]* %buf to i8* - %8 = bitcast [64 x i8]* %str to i8* + %8 = bitcast [3 x i8]* %str to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %8, i64 64, i1 false) br label %done right: ; preds = %entry - %9 = bitcast [64 x i8]* %str1 to i8* + %9 = bitcast [3 x i8]* %str1 to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %9) - store [64 x i8] c"hi\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str1, align 1 + store [3 x i8] c"hi\00", [3 x i8]* %str1, align 1 %10 = bitcast [64 x i8]* %buf to i8* - %11 = bitcast [64 x i8]* %str1 to i8* + %11 = bitcast [3 x i8]* %str1 to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %10, i8* align 1 %11, i64 64, i1 false) br label %done done: ; preds = %right, %left - %12 = bitcast [64 x i8]* %str1 to i8* + %12 = bitcast [3 x i8]* %str1 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) - %13 = bitcast [64 x i8]* %str to i8* + %13 = bitcast [3 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %13) %14 = bitcast i64* %"@x_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %14) diff -Nru bpftrace-0.14.1/tests/codegen/llvm/tuple.ll bpftrace-0.15.0/tests/codegen/llvm/tuple.ll --- bpftrace-0.14.1/tests/codegen/llvm/tuple.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/tuple.ll 2022-05-24 10:41:38.000000000 +0000 @@ -3,7 +3,7 @@ target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" target triple = "bpf-pc-linux" -%"int64_int64_string[64]__tuple_t" = type { i64, i64, [64 x i8] } +%"int64_int64_string[4]__tuple_t" = type { i64, i64, [4 x i8] } ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 @@ -11,33 +11,33 @@ define i64 @"kprobe:f"(i8* %0) section "s_kprobe:f_1" { entry: %"@t_key" = alloca i64, align 8 - %str = alloca [64 x i8], align 1 - %tuple = alloca %"int64_int64_string[64]__tuple_t", align 8 - %1 = bitcast %"int64_int64_string[64]__tuple_t"* %tuple to i8* + %str = alloca [4 x i8], align 1 + %tuple = alloca %"int64_int64_string[4]__tuple_t", align 8 + %1 = bitcast %"int64_int64_string[4]__tuple_t"* %tuple to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %1) - %2 = bitcast %"int64_int64_string[64]__tuple_t"* %tuple to i8* - call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 80, i1 false) - %3 = getelementptr %"int64_int64_string[64]__tuple_t", %"int64_int64_string[64]__tuple_t"* %tuple, i32 0, i32 0 + %2 = bitcast %"int64_int64_string[4]__tuple_t"* %tuple to i8* + call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 24, i1 false) + %3 = getelementptr %"int64_int64_string[4]__tuple_t", %"int64_int64_string[4]__tuple_t"* %tuple, i32 0, i32 0 store i64 1, i64* %3, align 8 - %4 = getelementptr %"int64_int64_string[64]__tuple_t", %"int64_int64_string[64]__tuple_t"* %tuple, i32 0, i32 1 + %4 = getelementptr %"int64_int64_string[4]__tuple_t", %"int64_int64_string[4]__tuple_t"* %tuple, i32 0, i32 1 store i64 2, i64* %4, align 8 - %5 = bitcast [64 x i8]* %str to i8* + %5 = bitcast [4 x i8]* %str to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5) - store [64 x i8] c"str\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [64 x i8]* %str, align 1 - %6 = getelementptr %"int64_int64_string[64]__tuple_t", %"int64_int64_string[64]__tuple_t"* %tuple, i32 0, i32 2 - %7 = bitcast [64 x i8]* %6 to i8* - %8 = bitcast [64 x i8]* %str to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %8, i64 64, i1 false) - %9 = bitcast [64 x i8]* %str to i8* + store [4 x i8] c"str\00", [4 x i8]* %str, align 1 + %6 = getelementptr %"int64_int64_string[4]__tuple_t", %"int64_int64_string[4]__tuple_t"* %tuple, i32 0, i32 2 + %7 = bitcast [4 x i8]* %6 to i8* + %8 = bitcast [4 x i8]* %str to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %8, i64 4, i1 false) + %9 = bitcast [4 x i8]* %str to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) %10 = bitcast i64* %"@t_key" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@t_key", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) - %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %"int64_int64_string[64]__tuple_t"*, i64)*)(i64 %pseudo, i64* %"@t_key", %"int64_int64_string[64]__tuple_t"* %tuple, i64 0) + %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, %"int64_int64_string[4]__tuple_t"*, i64)*)(i64 %pseudo, i64* %"@t_key", %"int64_int64_string[4]__tuple_t"* %tuple, i64 0) %11 = bitcast i64* %"@t_key" to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %11) - %12 = bitcast %"int64_int64_string[64]__tuple_t"* %tuple to i8* + %12 = bitcast %"int64_int64_string[4]__tuple_t"* %tuple to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret i64 0 } diff -Nru bpftrace-0.14.1/tests/codegen/llvm/variable_assign_array.ll bpftrace-0.15.0/tests/codegen/llvm/variable_assign_array.ll --- bpftrace-0.14.1/tests/codegen/llvm/variable_assign_array.ll 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/llvm/variable_assign_array.ll 2022-05-24 10:41:38.000000000 +0000 @@ -26,15 +26,15 @@ call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7) %probe_read_kernel = call i64 inttoptr (i64 113 to i64 (i32*, i32, i64)*)(i32* %array_access, i32 4, i64 %6) %8 = load i32, i32* %array_access, align 4 - %9 = sext i32 %8 to i64 - %10 = bitcast i32* %array_access to i8* - call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10) - %11 = bitcast i64* %"@x_key" to i8* - call void @llvm.lifetime.start.p0i8(i64 -1, i8* %11) + %9 = bitcast i32* %array_access to i8* + call void @llvm.lifetime.end.p0i8(i64 -1, i8* %9) + %10 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* %10) store i64 0, i64* %"@x_key", align 8 + %11 = sext i32 %8 to i64 %12 = bitcast i64* %"@x_val" to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12) - store i64 %9, i64* %"@x_val", align 8 + store i64 %11, i64* %"@x_val", align 8 %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 0) %update_elem = call i64 inttoptr (i64 2 to i64 (i64, i64*, i64*, i64)*)(i64 %pseudo, i64* %"@x_key", i64* %"@x_val", i64 0) %13 = bitcast i64* %"@x_val" to i8* diff -Nru bpftrace-0.14.1/tests/codegen/regression.cpp bpftrace-0.15.0/tests/codegen/regression.cpp --- bpftrace-0.14.1/tests/codegen/regression.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/codegen/regression.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -16,15 +16,15 @@ ASSERT_EQ(driver.parse_str("t:sched:sched_one* { cat(\"%s\", probe); }"), 0); bpftrace->feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, *bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), *bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::ResourceAnalyser resource_analyser(driver.root_); + ast::ResourceAnalyser resource_analyser(driver.root.get()); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; - ast::CodegenLLVM codegen(driver.root_, *bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), *bpftrace); codegen.compile(); } diff -Nru bpftrace-0.14.1/tests/log.cpp bpftrace-0.15.0/tests/log.cpp --- bpftrace-0.14.1/tests/log.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/log.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -14,13 +14,6 @@ const std::string content_1 = "hello world"; const std::string content_2 = "some messages 100###**"; - // clang-format off - LOG(DEBUG, ss) << content_1; std::string file = __FILE__; int line = __LINE__; - // clang-format on - std::string prefix = "[" + file + ":" + std::to_string(line) + "] "; - EXPECT_EQ(ss.str(), "DEBUG: " + prefix + content_1 + "\n"); - ss.str({}); - LOG(WARNING, ss) << content_1 << content_2; EXPECT_EQ(ss.str(), "WARNING: " + content_1 + content_2 + "\n"); ss.str({}); diff -Nru bpftrace-0.14.1/tests/parser.cpp bpftrace-0.15.0/tests/parser.cpp --- bpftrace-0.14.1/tests/parser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/parser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -33,7 +33,7 @@ std::ostringstream out; Printer printer(out); - printer.print(driver.root_); + printer.print(driver.root.get()); EXPECT_EQ(output, out.str()); } @@ -990,6 +990,7 @@ test_parse_failure("uprobe:f { 1 }"); test_parse_failure("uprobe { 1 }"); + test_parse_failure("uprobe:/my/program*:0x1234 { 1 }"); } TEST(Parser, usdt) diff -Nru bpftrace-0.14.1/tests/portability_analyser.cpp bpftrace-0.15.0/tests/portability_analyser.cpp --- bpftrace-0.14.1/tests/portability_analyser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/portability_analyser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -23,18 +23,18 @@ ASSERT_EQ(driver.parse_str(input), 0); - ast::FieldAnalyser fields(driver.root_, bpftrace, out); + ast::FieldAnalyser fields(driver.root.get(), bpftrace, out); ASSERT_EQ(fields.analyse(), 0) << msg.str() << out.str(); ClangParser clang; - ASSERT_TRUE(clang.parse(driver.root_, bpftrace)); + ASSERT_TRUE(clang.parse(driver.root.get(), bpftrace)); ASSERT_EQ(driver.parse_str(input), 0); out.str(""); - ast::SemanticAnalyser semantics(driver.root_, bpftrace, out, false); + ast::SemanticAnalyser semantics(driver.root.get(), bpftrace, out, false); ASSERT_EQ(semantics.analyse(), 0) << msg.str() << out.str(); - ast::PortabilityAnalyser portability(driver.root_, out); + ast::PortabilityAnalyser portability(driver.root.get(), out); EXPECT_EQ(portability.analyse(), expected_result) << msg.str() << out.str(); } diff -Nru bpftrace-0.14.1/tests/probe.cpp bpftrace-0.15.0/tests/probe.cpp --- bpftrace-0.14.1/tests/probe.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/probe.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -28,23 +28,23 @@ ASSERT_EQ(driver.parse_str(input), 0); - ast::FieldAnalyser fields(driver.root_, *bpftrace); + ast::FieldAnalyser fields(driver.root.get(), *bpftrace); EXPECT_EQ(fields.analyse(), 0); ClangParser clang; - clang.parse(driver.root_, *bpftrace); + clang.parse(driver.root.get(), *bpftrace); // Override to mockbpffeature. bpftrace->feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, *bpftrace); + ast::SemanticAnalyser semantics(driver.root.get(), *bpftrace); ASSERT_EQ(semantics.analyse(), 0); - ast::ResourceAnalyser resource_analyser(driver.root_); + ast::ResourceAnalyser resource_analyser(driver.root.get()); auto resources = resource_analyser.analyse(); ASSERT_EQ(resources.create_maps(*bpftrace, true), 0); bpftrace->resources = resources; - ast::CodegenLLVM codegen(driver.root_, *bpftrace); + ast::CodegenLLVM codegen(driver.root.get(), *bpftrace); codegen.generate_ir(); codegen.DumpIR(out); } diff -Nru bpftrace-0.14.1/tests/README.md bpftrace-0.15.0/tests/README.md --- bpftrace-0.14.1/tests/README.md 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/README.md 2022-05-24 10:41:38.000000000 +0000 @@ -1,10 +1,10 @@ # bpftrace Tests -There are two test suites in the project. - ## Unit tests These tests can be run with the `bpftrace_test` executable. +Tests can be selected with the `--gtest_filter` flag or the `GTEST_FILTER` +environment variable, see `--help` for more information. ### Codegen tests @@ -64,7 +64,7 @@ * `MIN_KERNEL`: Skip the test unless the host's kernel version is >= the provided kernel version. Try not to use this directive as kernel versions may be misleading (backported kernel features, for example) -* `REQUIREMENT`: Run a command in a shell. If it succeeds, run the testcase. +* `REQUIRES`: Run a command in a shell. If it succeeds, run the testcase. Else, skip the testcase. * `ENV`: Run bpftrace invocation with additional environment variables. Must be in format NAME=VALUE. Supports multiple values separated by spaces. @@ -73,6 +73,7 @@ * `REQUIRES_FEATURE`: Only run testcase if the following bpftrace feature is built in. See `bpftrace --info` and `runtime/engine/runner.py` for more details. Also supports negative features (by prefixing `!` before feature). +* `WILL_FAIL`: Mark that this test case will exit uncleanly (ie exit code != 0) If you need to run a test program to probe (eg, uprobe/USDT), you can use the `BEFORE` clause. The test scripts will wait for the test program to have a pid. @@ -108,3 +109,21 @@ This is intended to be useful for testing uprobes and USDT probes, or using uprobes to verify some other behavior in bpftrace. It can also be used to tightly control what code paths are triggered in the system. + +## Tool parsing tests + +`./tests/tools-parsing-test.sh` + +The tool parsing tests ensure that the tools shipped with bpftrace are valid and +can run. The actual output is not validated. + +### Flags and variables + +The following environment variables can be set to modify the behaviour of the +test suite + +- `BPFTRACE_EXECUTABLE`: location of the bpftrace executable, if left unset the + script attempts to autodetect it. +- `TOOLS_TEST_DISABLE`: comma separated list of tools to skip, e.g. + `vfscount.bt,swapin.bt` +- `TOOLS_TEST_OLDVERSION`: tests the tools/old version of these tools instead. diff -Nru bpftrace-0.14.1/tests/required_resources.cpp bpftrace-0.15.0/tests/required_resources.cpp --- bpftrace-0.14.1/tests/required_resources.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/required_resources.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -5,6 +5,7 @@ #include +#include "format_string.h" #include "struct.h" #include "types.h" @@ -39,7 +40,7 @@ std::ostringstream serialized(std::ios::binary); { RequiredResources r; - r.system_args.emplace_back("field0", + r.system_args.emplace_back(FormatString("field0"), std::vector{ Field{ .name = "myfield", .type = CreateInt32(), @@ -61,7 +62,7 @@ r.load_state(input); ASSERT_EQ(r.system_args.size(), 1ul); - EXPECT_EQ(std::get<0>(r.system_args[0]), "field0"); + EXPECT_EQ(std::get<0>(r.system_args[0]).str(), "field0"); auto &fields = std::get<1>(r.system_args[0]); ASSERT_EQ(fields.size(), 1ul); diff -Nru bpftrace-0.14.1/tests/runtime/array bpftrace-0.15.0/tests/runtime/array --- bpftrace-0.14.1/tests/runtime/array 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/array 2022-05-24 10:41:38.000000000 +0000 @@ -15,6 +15,7 @@ EXPECT the index 5 is out of bounds for array of size 4 TIMEOUT 5 AFTER ./testprogs/array_access +WILL_FAIL NAME array element access via assignment into var PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = ((struct A *) arg0)->x; $x = $a[0]; printf("Result: %d\n", $x); exit(); } diff -Nru bpftrace-0.14.1/tests/runtime/banned_probes bpftrace-0.15.0/tests/runtime/banned_probes --- bpftrace-0.14.1/tests/runtime/banned_probes 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/banned_probes 2022-05-24 10:41:38.000000000 +0000 @@ -2,18 +2,22 @@ PROG kretprobe:_raw_spin_lock { exit(); } EXPECT error: kretprobe:_raw_spin_lock can't be used as it might lock up your system. TIMEOUT 1 +WILL_FAIL NAME kretprobe:queued_spin_lock_slowpath is banned PROG kretprobe:queued_spin_lock_slowpath { exit(); } EXPECT error: kretprobe:queued_spin_lock_slowpath can't be used as it might lock up your system. TIMEOUT 1 +WILL_FAIL NAME kretprobe:_raw_spin_unlock_irqrestore is banned PROG kretprobe:_raw_spin_unlock_irqrestore { exit(); } EXPECT error: kretprobe:_raw_spin_unlock_irqrestore can't be used as it might lock up your system. TIMEOUT 1 +WILL_FAIL NAME kretprobe:_raw_spin_lock_irqsave is banned PROG kretprobe:_raw_spin_lock_irqsave { exit(); } EXPECT error: kretprobe:_raw_spin_lock_irqsave can't be used as it might lock up your system. TIMEOUT 1 +WILL_FAIL diff -Nru bpftrace-0.14.1/tests/runtime/basic bpftrace-0.15.0/tests/runtime/basic --- bpftrace-0.14.1/tests/runtime/basic 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/basic 2022-05-24 10:41:38.000000000 +0000 @@ -12,11 +12,13 @@ RUN {{BPFTRACE}} -idonotexist EXPECT USAGE TIMEOUT 1 +WILL_FAIL NAME errors on non existent file RUN {{BPFTRACE}} non_existent_file.bt EXPECT ERROR: failed to open file 'non_existent_file.bt': No such file or directory TIMEOUT 1 +WILL_FAIL NAME piped script RUN {{BPFTRACE}} - < runtime/scripts/hello_world.bt @@ -47,12 +49,14 @@ RUN {{BPFTRACE}} -l | grep kfunc EXPECT kfunc REQUIRES_FEATURE btf +REQUIRES_FEATURE kfunc TIMEOUT 1 NAME it lists kfunc params RUN {{BPFTRACE}} -lv "kfunc:*" | grep kfunc EXPECT \s+[a-zA-Z_\*\s]+ REQUIRES_FEATURE btf +REQUIRES_FEATURE kfunc TIMEOUT 1 NAME it lists kprobes with regex filter @@ -94,12 +98,14 @@ RUN {{BPFTRACE}} -l "kfunc:*" EXPECT kfunc REQUIRES_FEATURE btf +REQUIRES_FEATURE kfunc TIMEOUT 1 NAME it lists kretfuncs events with regex filter RUN {{BPFTRACE}} -l "kretfunc:*" EXPECT kretfunc REQUIRES_FEATURE btf +REQUIRES_FEATURE kfunc TIMEOUT 1 NAME listing with wildcarded probe type @@ -112,36 +118,43 @@ RUN {{BPFTRACE}} -l '\n' EXPECT ERROR: invalid character TIMEOUT 1 +WILL_FAIL NAME pid fails validation with leading non-number -RUN {{BPFTRACE}} -p a1111 +RUN {{BPFTRACE}} -p a1111 file.bt EXPECT ERROR: pid 'a1111' is not a valid decimal number TIMEOUT 1 +WILL_FAIL NAME pid fails validation with non-number in between -RUN {{BPFTRACE}} -p 111a1 +RUN {{BPFTRACE}} -p 111a1 file.bt EXPECT ERROR: pid '111a1' is not a valid decimal number TIMEOUT 1 +WILL_FAIL NAME pid fails validation with non-numeric argument -RUN {{BPFTRACE}} -p not_a_pid +RUN {{BPFTRACE}} -p not_a_pid file.bt EXPECT ERROR: pid 'not_a_pid' is not a valid decimal number TIMEOUT 1 +WILL_FAIL NAME pid outside of valid pid range -RUN {{BPFTRACE}} -p 5000000 +RUN {{BPFTRACE}} -p 5000000 file.bt EXPECT ERROR: pid '5000000' out of valid pid range \[1,4194304\] TIMEOUT 1 +WILL_FAIL NAME libraries under /usr/include are in the search path -RUN {{BPFTRACE}} -e "$(echo "#include "; echo "BEGIN { exit(); }")" 2>&1 +RUN {{BPFTRACE}} -e "$(echo "#include "; echo "BEGIN { exit(); }")" 2>&1 EXPECT ^((?!file not found).)*$ +REQUIRES ls /usr/include/sys/xattr.h TIMEOUT 1 NAME non existent library include fails RUN {{BPFTRACE}} -e "$(echo "#include "; echo "BEGIN { exit(); }")" 2>&1 EXPECT file not found TIMEOUT 1 +WILL_FAIL NAME defines work RUN {{BPFTRACE}} -e "$(echo '#define _UNDERSCORE 314'; echo 'BEGIN { printf("%d\\n", _UNDERSCORE); exit(); }')" @@ -181,7 +194,7 @@ NAME spawn child RUN {{BPFTRACE}} -e 'i:ms:500 { printf("%d\n", cpid); }' -c './testprogs/syscall nanosleep 1e9' EXPECT [0-9]+ -TIMEOUT 1 +TIMEOUT 3 NAME info flag RUN {{BPFTRACE}} --info @@ -198,8 +211,10 @@ RUN {{BPFTRACE}} --no-warnings -e 'BEGIN { @x = stats(10); print(@x, 2); clear(@x); exit();}' 2>&1| grep -c -E "WARNING|invalid option" EXPECT ^0$ TIMEOUT 1 +WILL_FAIL NAME kaddr fails PROG BEGIN { print(kaddr("asdfzzzzzzz")) } EXPECT Failed to resolve kernel symbol TIMEOUT 1 +WILL_FAIL diff -Nru bpftrace-0.14.1/tests/runtime/btf bpftrace-0.15.0/tests/runtime/btf --- bpftrace-0.14.1/tests/runtime/btf 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/btf 2022-05-24 10:41:38.000000000 +0000 @@ -23,7 +23,7 @@ REQUIRES_FEATURE btf NAME redefine_btf_type -PROG struct task_struct { int x; } BEGIN { printf("%d\n", curtask->x); } +PROG struct task_struct { int x; } BEGIN { printf("%d\n", curtask->x); exit() } EXPECT -?[0-9][0-9]* TIMEOUT 5 REQUIRES_FEATURE btf @@ -33,3 +33,4 @@ EXPECT error:.*'struct thread_info' TIMEOUT 5 REQUIRES_FEATURE btf +WILL_FAIL diff -Nru bpftrace-0.14.1/tests/runtime/builtin bpftrace-0.15.0/tests/runtime/builtin --- bpftrace-0.14.1/tests/runtime/builtin 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/builtin 2022-05-24 10:41:38.000000000 +0000 @@ -1,98 +1,92 @@ NAME pid -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", pid); exit(); } -EXPECT SUCCESS pid [0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", pid); exit(); } +EXPECT SUCCESS [0-9][0-9]* TIMEOUT 5 NAME tid -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", tid); exit(); } -EXPECT SUCCESS tid [0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", tid); exit(); } +EXPECT SUCCESS [0-9][0-9]* TIMEOUT 5 NAME uid -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", uid); exit(); } -EXPECT SUCCESS uid [0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", uid); exit(); } +EXPECT SUCCESS [0-9][0-9]* TIMEOUT 5 NAME gid -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", gid); exit(); } -EXPECT SUCCESS gid [0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", gid); exit(); } +EXPECT SUCCESS [0-9][0-9]* TIMEOUT 5 NAME nsecs -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", nsecs); exit(); } -EXPECT SUCCESS nsecs -?[0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", nsecs); exit(); } +EXPECT SUCCESS -?[0-9][0-9]* TIMEOUT 5 NAME elapsed -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", elapsed); exit(); } -EXPECT SUCCESS elapsed -?[0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", elapsed); exit(); } +EXPECT SUCCESS -?[0-9][0-9]* TIMEOUT 5 NAME cpu -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", cpu); exit(); } -EXPECT SUCCESS cpu -?[0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", cpu); exit(); } +EXPECT SUCCESS -?[0-9][0-9]* TIMEOUT 5 NAME comm -PROG k:vfs_read { printf("SUCCESS '$test' %s\n", comm); exit(); } -EXPECT SUCCESS comm .* -TIMEOUT 5 -AFTER ./testprogs/syscall read - -NAME stack -PROG k:vfs_read{ printf("SUCCESS '$test' %s\n", stack); exit(); } -EXPECT SUCCESS stack +PROG k:vfs_read { printf("SUCCESS %s\n", comm); exit(); } +EXPECT SUCCESS .* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kstack -PROG k:vfs_read{ printf("SUCCESS '$test' %s\n", kstack); exit(); } -EXPECT SUCCESS kstack +PROG k:vfs_read{ printf("%s\n", kstack); exit(); } +EXPECT Attaching 1 probe TIMEOUT 5 AFTER ./testprogs/syscall read NAME ustack -PROG k:vfs_read { printf("SUCCESS '$test' %s\n", ustack); exit(); } -EXPECT SUCCESS ustack +PROG k:vfs_read { printf("%s\n", ustack); exit(); } +EXPECT Attaching 1 probe TIMEOUT 5 AFTER ./testprogs/syscall read NAME arg -PROG k:vfs_read { printf("SUCCESS '$test' %d\n", arg0); exit(); } -EXPECT SUCCESS arg -?[0-9][0-9]* +PROG k:vfs_read { printf("SUCCESS %d\n", arg0); exit(); } +EXPECT SUCCESS -?[0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME sarg -PROG uprobe:./testprogs/stack_args:too_many_args { printf("SUCCESS '$test' %d %d\n", sarg0, sarg1); exit(); } -EXPECT SUCCESS sarg 32 64 +PROG uprobe:./testprogs/stack_args:too_many_args { printf("SUCCESS %d %d\n", sarg0, sarg1); exit(); } +EXPECT SUCCESS 32 64 ARCH x86_64 TIMEOUT 5 AFTER ./testprogs/stack_args NAME sarg -PROG uprobe:./testprogs/stack_args:too_many_args { printf("SUCCESS '$test' %d %d\n", sarg0, sarg1); exit(); } -EXPECT SUCCESS sarg 128 256 +PROG uprobe:./testprogs/stack_args:too_many_args { printf("SUCCESS %d %d\n", sarg0, sarg1); exit(); } +EXPECT SUCCESS 128 256 ARCH aarch64|ppc64|ppc64le TIMEOUT 5 AFTER ./testprogs/stack_args NAME sarg -PROG uprobe:./testprogs/stack_args:too_many_args { printf("SUCCESS '$test' %d %d\n", sarg0, sarg1); exit(); } -EXPECT SUCCESS sarg 16 32 +PROG uprobe:./testprogs/stack_args:too_many_args { printf("SUCCESS %d %d\n", sarg0, sarg1); exit(); } +EXPECT SUCCESS 16 32 ARCH s390x TIMEOUT 5 AFTER ./testprogs/stack_args NAME retval -PROG kretprobe:vfs_read { printf("SUCCESS '$test' %d\n", retval); exit(); } -EXPECT SUCCESS retval .* +PROG kretprobe:vfs_read { printf("SUCCESS %d\n", retval); exit(); } +EXPECT SUCCESS .* TIMEOUT 5 AFTER ./testprogs/syscall read NAME func -PROG k:vfs_read { printf("SUCCESS '$test' %s\n", func); exit(); } -EXPECT SUCCESS func .* +PROG k:vfs_read { printf("SUCCESS %s\n", func); exit(); } +EXPECT SUCCESS .* TIMEOUT 5 AFTER ./testprogs/syscall read @@ -103,13 +97,13 @@ TIMEOUT 5 NAME username -PROG i:ms:1 { printf("SUCCESS '$test' %s\n", username); exit(); } -EXPECT SUCCESS username .* +PROG i:ms:1 { printf("SUCCESS %s\n", username); exit(); } +EXPECT SUCCESS .* TIMEOUT 5 NAME probe -PROG k:do_nanosleep { printf("SUCCESS '$test' %s\n", probe); exit(); } -EXPECT SUCCESS probe kprobe:do_nanosleep +PROG k:do_nanosleep { printf("SUCCESS %s\n", probe); exit(); } +EXPECT SUCCESS kprobe:do_nanosleep TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 @@ -120,29 +114,29 @@ AFTER ./testprogs/syscall nanosleep 1e8 NAME curtask -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", curtask); exit(); } -EXPECT SUCCESS curtask -?[0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", curtask); exit(); } +EXPECT SUCCESS -?[0-9][0-9]* TIMEOUT 5 NAME curtask_field -PROG struct task_struct {int x;} i:ms:1 { printf("SUCCESS '$test' %d\n", curtask->x); exit(); } -EXPECT SUCCESS curtask_field -?[0-9][0-9]* +PROG struct task_struct {int x;} i:ms:1 { printf("SUCCESS %d\n", curtask->x); exit(); } +EXPECT SUCCESS -?[0-9][0-9]* TIMEOUT 5 NAME rand -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", rand); exit(); } -EXPECT SUCCESS rand -?[0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", rand); exit(); } +EXPECT SUCCESS -?[0-9][0-9]* TIMEOUT 5 NAME cgroup -PROG i:ms:1 { printf("SUCCESS '$test' %d\n", cgroup); exit(); } -EXPECT SUCCESS cgroup -?[0-9][0-9]* +PROG i:ms:1 { printf("SUCCESS %d\n", cgroup); exit(); } +EXPECT SUCCESS -?[0-9][0-9]* TIMEOUT 5 MIN_KERNEL 4.18 NAME ctx -PROG struct x {unsigned long x}; i:ms:1 { printf("SUCCESS '$test' %d\n", ((struct x*)ctx)->x); exit(); } -EXPECT SUCCESS ctx -?[0-9][0-9]* +PROG struct x {unsigned long x}; i:ms:1 { printf("SUCCESS %d\n", ((struct x*)ctx)->x); exit(); } +EXPECT SUCCESS -?[0-9][0-9]* TIMEOUT 5 NAME cat @@ -166,6 +160,7 @@ RUN {{BPFTRACE}} -ve 'BEGIN { if (str($1) == str($2)) { printf("%s\n", str($1)); exit() } }' "hello" "hello" EXPECT Error loading program: BEGIN TIMEOUT 5 +WILL_FAIL NAME increase log size RUN {{BPFTRACE}} -ve 'BEGIN { if (str($1) == str($2)) { printf("%s\n", str($1)); exit() } }' "hello" "hello" diff -Nru bpftrace-0.14.1/tests/runtime/call bpftrace-0.15.0/tests/runtime/call --- bpftrace-0.14.1/tests/runtime/call 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/call 2022-05-24 10:41:38.000000000 +0000 @@ -39,13 +39,13 @@ TIMEOUT 5 NAME join -RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo 'A'"); } kprobe:sys_execve { join(arg1); exit();}' -EXPECT A +RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo '_A_'"); } t:syscalls:sys_enter_execve { join(args->argv); exit();}' +EXPECT _A_ TIMEOUT 5 NAME join_delim -RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo 'A'"); } kprobe:sys_execve { join(arg1, ","); exit();}' -EXPECT A +RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo '_A_'"); } t:syscalls:sys_enter_execve { join(args->argv, ","); exit();}' +EXPECT _A_ TIMEOUT 5 NAME str @@ -86,6 +86,11 @@ EXPECT @: ok_value TIMEOUT 5 +NAME buf_no_ascii +PROG BEGIN { printf("%rx", buf("Hello\0", 6)); exit(); } +EXPECT \\x48\\x65\\x6c\\x6c\\x6f\\x00 +TIMEOUT 5 + NAME ksym PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("ip"))); exit();} EXPECT do_nanosleep @@ -172,21 +177,21 @@ AFTER ./testprogs/syscall read NAME kstack -PROG k:do_nanosleep { printf("SUCCESS '$test' %s\n%s\n", kstack(), kstack(1)); exit(); } -EXPECT SUCCESS kstack +PROG k:do_nanosleep { printf("%s\n%s\n", kstack(), kstack(1)); exit(); } +EXPECT Attaching 1 probe TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 NAME kstack perf mode -PROG k:do_nanosleep { printf("SUCCESS '$test' %s\n", kstack(perf, 1)); exit(); } -EXPECT SUCCESS kstack +PROG k:do_nanosleep { printf("%s\n", kstack(perf, 1)); exit(); } +EXPECT Attaching 1 probe TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 NAME ustack -PROG k:do_nanosleep { printf("SUCCESS '$test' %s\n%s\n", ustack(), ustack(1)); exit(); } -EXPECT SUCCESS ustack +PROG k:do_nanosleep { printf("%s\n%s\n", ustack(), ustack(1)); exit(); } +EXPECT Attaching 1 probe TIMEOUT 5 AFTER ./testprogs/syscall nanosleep 1e8 @@ -337,3 +342,47 @@ EXPECT OK REQUIRES_FEATURE iter:task_file TIMEOUT 5 + +NAME cgroup_path +PROG BEGIN { print(cgroup_path(cgroup & ((1 << 32) - 1))); exit(); } +EXPECT ([a-z]*:\/.*;)*[a-z]*:\/.* +REQUIRES grep -q '^cgroup2' /proc/mounts +TIMEOUT 5 +MIN_KERNEL 4.18 + +NAME cgroup_path printf +PROG BEGIN { printf("path: %s", cgroup_path(cgroup & ((1 << 32) - 1))); exit(); } +EXPECT path: ([a-z]*:\/.*;)*[a-z]*:\/.* +REQUIRES grep -q '^cgroup2' /proc/mounts +TIMEOUT 5 +MIN_KERNEL 4.18 + +NAME bswap_int64 +RUN {{BPFTRACE}} -e 'BEGIN { printf("%lx", bswap(0x1122334455667788)); exit(); }' +EXPECT 8877665544332211 +TIMEOUT 1 + +NAME bswap_int32 +RUN {{BPFTRACE}} -e 'BEGIN { printf("%x", bswap((int32)0x12345678)); exit(); }' +EXPECT 78563412 +TIMEOUT 1 + +NAME bswap_int16 +RUN {{BPFTRACE}} -e 'BEGIN { printf("%x", bswap((int16)0x1234)); exit(); }' +EXPECT 3412 +TIMEOUT 1 + +NAME bswap_int8 +RUN {{BPFTRACE}} -e 'BEGIN { printf("%x", bswap((int8)0x12)); exit(); }' +EXPECT 12 +TIMEOUT 1 + +NAME bswap_int64_op +RUN {{BPFTRACE}} -e 'BEGIN { printf("%lx", bswap(0x12340000 + 0x5678)); exit(); }' +EXPECT 7856341200000000 +TIMEOUT 1 + +NAME bswap_twice +RUN {{BPFTRACE}} -e 'BEGIN { printf("%x", bswap(bswap(0x1234))); exit(); }' +EXPECT 1234 +TIMEOUT 1 \ No newline at end of file diff -Nru bpftrace-0.14.1/tests/runtime/dwarf bpftrace-0.15.0/tests/runtime/dwarf --- bpftrace-0.14.1/tests/runtime/dwarf 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/dwarf 2022-05-24 10:41:38.000000000 +0000 @@ -10,7 +10,7 @@ REQUIRES_FEATURE dwarf TIMEOUT 5 -NAME list uprobe args - pointer type +NAME list uprobe args - struct pointer type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test:function2' EXPECT struct Foo\* foo1 REQUIRES_FEATURE dwarf @@ -29,3 +29,38 @@ REQUIRES_FEATURE dwarf TIMEOUT 5 BEFORE ./testprogs/uprobe_test + +NAME uprobe arg by name - struct +PROG uprobe:./testprogs/uprobe_test:function2 { printf("foo1->a = %d\n", args->foo1->a); exit(); } +EXPECT foo1->a = 123 +REQUIRES_FEATURE dwarf +TIMEOUT 5 +BEFORE ./testprogs/uprobe_test + +NAME struct field string +PROG uprobe:./testprogs/uprobe_test:function2 { printf("foo1->b = %s\n", args->foo1->b); exit(); } +EXPECT foo1->b = hello +REQUIRES_FEATURE dwarf +TIMEOUT 5 +BEFORE ./testprogs/uprobe_test + +NAME struct field array +PROG uprobe:./testprogs/uprobe_test:function2 { print(args->foo1->c); exit(); } +EXPECT \[1,2,3\] +REQUIRES_FEATURE dwarf +TIMEOUT 5 +BEFORE ./testprogs/uprobe_test + +NAME cast to struct +PROG uprobe:./testprogs/uprobe_test:function2 { printf("foo1->a = %d\n", ((struct Foo *)arg0)->a); exit(); } +EXPECT foo1->a = 123 +REQUIRES_FEATURE dwarf +TIMEOUT 5 +BEFORE ./testprogs/uprobe_test + +NAME struct override +PROG struct Foo { int b; } uprobe:./testprogs/uprobe_test:function2 { printf("foo1->b = %d\n", ((struct Foo *)arg0)->b); exit(); } +EXPECT foo1->b = 123 +REQUIRES_FEATURE dwarf +TIMEOUT 5 +BEFORE ./testprogs/uprobe_test diff -Nru bpftrace-0.14.1/tests/runtime/engine/parser.py bpftrace-0.15.0/tests/runtime/engine/parser.py --- bpftrace-0.14.1/tests/runtime/engine/parser.py 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/engine/parser.py 2022-05-24 10:41:38.000000000 +0000 @@ -28,12 +28,14 @@ 'before', 'after', 'suite', - 'kernel', + 'kernel_min', + 'kernel_max', 'requirement', 'env', 'arch', 'feature_requirement', 'neg_feature_requirement', + 'will_fail' ], ) @@ -101,12 +103,14 @@ timeout = '' before = '' after = '' - kernel = '' + kernel_min = '' + kernel_max = '' requirement = '' env = {} arch = [] feature_requirement = set() neg_feature_requirement = set() + will_fail = False for item in test: item_split = item.split() @@ -128,7 +132,9 @@ elif item_name == 'AFTER': after = line elif item_name == 'MIN_KERNEL': - kernel = line + kernel_min = line + elif item_name == 'MAX_KERNEL': + kernel_max = line elif item_name == 'REQUIRES': requirement = line elif item_name == 'ENV': @@ -141,9 +147,11 @@ features = { "loop", "btf", + "kfunc", "probe_read_kernel", "dpath", "uprobe_refcount", + "bcc_usdt_addsem", "signal", "iter:task", "iter:task_file", @@ -162,6 +170,8 @@ unknown = (feature_requirement | neg_feature_requirement) - features if len(unknown) > 0: raise UnknownFieldError('%s is invalid for REQUIRES_FEATURE. Suite: %s' % (','.join(unknown), test_suite)) + elif item_name == "WILL_FAIL": + will_fail = True else: raise UnknownFieldError('Field %s is unknown. Suite: %s' % (item_name, test_suite)) @@ -185,9 +195,11 @@ before, after, test_suite, - kernel, + kernel_min, + kernel_max, requirement, env, arch, feature_requirement, - neg_feature_requirement) + neg_feature_requirement, + will_fail) diff -Nru bpftrace-0.14.1/tests/runtime/engine/runner.py bpftrace-0.15.0/tests/runtime/engine/runner.py --- bpftrace-0.14.1/tests/runtime/engine/runner.py 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/engine/runner.py 2022-05-24 10:41:38.000000000 +0000 @@ -2,6 +2,7 @@ import subprocess import signal +import sys import os import time from os import environ, uname, devnull @@ -24,7 +25,7 @@ # TODO(mmarchini) only add colors if terminal supports it def colorify(s, color): - return "%s%s%s" % (color, s, NO_COLOR) + return "%s%s%s" % (color, s, NO_COLOR) if sys.stdout.isatty() else s def ok(s): return colorify(s, OK_COLOR) @@ -41,12 +42,13 @@ class Runner(object): PASS = 0 FAIL = 1 - SKIP_KERNEL_VERSION = 2 + SKIP_KERNEL_VERSION_MIN = 2 TIMEOUT = 3 SKIP_REQUIREMENT_UNSATISFIED = 4 SKIP_ENVIRONMENT_DISABLED = 5 SKIP_FEATURE_REQUIREMENT_UNSATISFIED = 6 SKIP_AOT_NOT_SUPPORTED = 7 + SKIP_KERNEL_VERSION_MAX = 8 @staticmethod def failed(status): @@ -55,7 +57,8 @@ @staticmethod def skipped(status): return status in [ - Runner.SKIP_KERNEL_VERSION, + Runner.SKIP_KERNEL_VERSION_MIN, + Runner.SKIP_KERNEL_VERSION_MAX, Runner.SKIP_REQUIREMENT_UNSATISFIED, Runner.SKIP_ENVIRONMENT_DISABLED, Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED, @@ -64,8 +67,10 @@ @staticmethod def skip_reason(test, status): - if status == Runner.SKIP_KERNEL_VERSION: - return "min Kernel: %s" % test.kernel + if status == Runner.SKIP_KERNEL_VERSION_MIN: + return "min Kernel: %s" % test.kernel_min + if status == Runner.SKIP_KERNEL_VERSION_MAX: + return "max Kernel: %s" % test.kernel_max elif status == Runner.SKIP_REQUIREMENT_UNSATISFIED: return "unmet condition: '%s'" % test.requirement elif status == Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED: @@ -120,9 +125,11 @@ bpffeature["loop"] = output.find("Loop support: yes") != -1 bpffeature["probe_read_kernel"] = output.find("probe_read_kernel: yes") != -1 bpffeature["btf"] = output.find("btf (depends on Build:libbpf): yes") != -1 + bpffeature["kfunc"] = output.find("kfunc: yes") != -1 bpffeature["dpath"] = output.find("dpath: yes") != -1 bpffeature["uprobe_refcount"] = \ output.find("uprobe refcount (depends on Build:bcc bpf_attach_uprobe refcount): yes") != -1 + bpffeature["bcc_usdt_addsem"] = output.find("bcc_usdt_addsem: yes") != -1 bpffeature["signal"] = output.find("send_signal: yes") != -1 bpffeature["iter:task"] = output.find("iter:task: yes") != -1 bpffeature["iter:task_file"] = output.find("iter:task_file: yes") != -1 @@ -134,9 +141,13 @@ @staticmethod def run_test(test): current_kernel = LooseVersion(uname()[2]) - if test.kernel and LooseVersion(test.kernel) > current_kernel: + if test.kernel_min and LooseVersion(test.kernel_min) > current_kernel: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) - return Runner.SKIP_KERNEL_VERSION + return Runner.SKIP_KERNEL_VERSION_MIN + + if test.kernel_max and LooseVersion(test.kernel_max) < current_kernel: + print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) + return Runner.SKIP_KERNEL_VERSION_MAX full_test_name = test.suite + "." + test.name if full_test_name in os.getenv("RUNTIME_TEST_DISABLE", "").split(","): @@ -147,7 +158,9 @@ try: before = None + bpftrace = None after = None + timeout = False print(ok("[ RUN ] ") + "%s.%s" % (test.suite, test.name)) if test.requirement: @@ -213,6 +226,7 @@ universal_newlines=True, bufsize=1 ) + bpftrace = p signal.alarm(ATTACH_TIMEOUT) @@ -233,10 +247,20 @@ result = re.search(test.expect, output, re.M) except (TimeoutError): + # If bpftrace timed out (probably b/c the test case didn't explicitly + # terminate bpftrace), then we mark the test case as timed out so that + # we don't check the return code. The return code will probably not be + # clean b/c we ran the subprocess in shellout mode and the shell won't + # return a clean exit. + timeout = True + # Give it a last chance, the test might have worked but the # bpftrace process might still be alive + # + # Send a SIGTERM here so bpftrace exits cleanly. We'll send an SIGKILL + # if SIGTERM didn't do the trick later if p.poll() is None: - os.killpg(os.getpgid(p.pid), signal.SIGKILL) + os.killpg(os.getpgid(p.pid), signal.SIGTERM) output += p.communicate()[0] result = re.search(test.expect, output) if not result: @@ -249,9 +273,19 @@ if before and before.poll() is None: os.killpg(os.getpgid(before.pid), signal.SIGKILL) + if bpftrace and bpftrace.poll() is None: + os.killpg(os.getpgid(p.pid), signal.SIGKILL) + if after and after.poll() is None: os.killpg(os.getpgid(after.pid), signal.SIGKILL) + if p.returncode != 0 and not test.will_fail and not timeout: + print(fail("[ FAILED ] ") + "%s.%s" % (test.suite, test.name)) + print('\tCommand: ' + bpf_call) + print('\tUnclean exit code: ' + str(p.returncode)) + print('\tOutput: ' + output.encode("unicode_escape").decode("utf-8")) + return Runner.FAIL + if result: print(ok("[ OK ] ") + "%s.%s" % (test.suite, test.name)) return Runner.PASS diff -Nru bpftrace-0.14.1/tests/runtime/json-output bpftrace-0.15.0/tests/runtime/json-output --- bpftrace-0.14.1/tests/runtime/json-output 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/json-output 2022-05-24 10:41:38.000000000 +0000 @@ -2,6 +2,7 @@ RUN {{BPFTRACE}} -q -f jsonx -e 'BEGIN { @scalar = 5; exit(); }' EXPECT ^ERROR: Invalid output format "jsonx"$ TIMEOUT 5 +WILL_FAIL NAME scalar RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @scalar = 5; exit(); }' | python3 -c 'import sys,json; print(json.load(sys.stdin) == json.load(open("runtime/outputs/scalar.json")))' diff -Nru bpftrace-0.14.1/tests/runtime/other bpftrace-0.15.0/tests/runtime/other --- bpftrace-0.14.1/tests/runtime/other 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/other 2022-05-24 10:41:38.000000000 +0000 @@ -62,11 +62,13 @@ PROG i:ms:1 {$a = 1; unroll (101) { $a = $a + 2; } printf("a=%d\n", $a); exit();} EXPECT unroll maximum value is 100 TIMEOUT 5 +WILL_FAIL NAME unroll_min_value PROG i:ms:1 {$a = 1; unroll (0) { $a = $a + 2; } printf("a=%d\n", $a); exit();} EXPECT unroll minimum value is 1 TIMEOUT 5 +WILL_FAIL NAME unroll_param RUN {{BPFTRACE}} -e 'i:ms:1 {$a = 1; unroll ($1) { $a = $a + 1; } printf("a=%d\n", $a); exit();}' 10 @@ -94,12 +96,12 @@ TIMEOUT 5 NAME positional string compare via variable - equal -RUN {{BPFTRACE}} -e 'BEGIN { $x = str($1); if ($x == "hello")) { printf("I got hello\n");} else { printf("not equal\n");} exit();}' "hello" +RUN {{BPFTRACE}} -e 'BEGIN { $x = str($1); if ($x == "hello") { printf("I got %s\n", "hello");} else { printf("not equal\n");} exit();}' "hello" EXPECT I got hello TIMEOUT 5 NAME positional string compare via variable - not equal -RUN {{BPFTRACE}} -e 'BEGIN { $x = str($1); if ($x == "hello")) { printf("I got hello\n");} else { printf("not equal\n");} exit();}' "hell" +RUN {{BPFTRACE}} -e 'BEGIN { $x = str($1); if ($x == "hello") { printf("I got hello\n");} else { printf("not %s\n", "equal");} exit();}' "hell" EXPECT not equal TIMEOUT 5 @@ -164,8 +166,8 @@ TIMEOUT 5 NAME positional multiple bases -RUN {{BPFTRACE}} -e 'BEGIN { printf("got: %d %d 0x%x\n", $1, $2, $3); exit() }' 123 0775 0x123 -EXPECT got: 123 509 0x123 +RUN {{BPFTRACE}} -e 'BEGIN { printf("got: %d %o 0x%x\n", $1, $2, $3); exit() }' 123 0775 0x123 +EXPECT got: 123 775 0x123 TIMEOUT 5 NAME positional pointer arithmetics diff -Nru bpftrace-0.14.1/tests/runtime/probe bpftrace-0.15.0/tests/runtime/probe --- bpftrace-0.14.1/tests/runtime/probe 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/probe 2022-05-24 10:41:38.000000000 +0000 @@ -1,19 +1,28 @@ +NAME kfunc_wildcard +PROG kfunc:vfs_re*ad { printf("SUCCESS %d\n", pid); exit(); } +EXPECT SUCCESS [0-9][0-9]* +REQUIRES_FEATURE btf +REQUIRES_FEATURE kfunc +TIMEOUT 5 +AFTER ./testprogs/syscall read + NAME kprobe -PROG kprobe:vfs_read { printf("SUCCESS '$test' %d\n", pid); exit(); } -EXPECT SUCCESS kprobe [0-9][0-9]* +PROG kprobe:vfs_read { printf("SUCCESS %d\n", pid); exit(); } +EXPECT SUCCESS [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kprobe_short_name -PROG k:vfs_read { printf("SUCCESS '$test' %d\n", pid); exit(); } -EXPECT SUCCESS kprobe_short_name [0-9][0-9]* +PROG k:vfs_read { printf("SUCCESS %d\n", pid); exit(); } +EXPECT SUCCESS [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kprobe_target -PROG kprobe:syscalls:sys_exit_nanosleep { printf("SUCCESS '$test' %d\n", pid); exit(); } +PROG kprobe:syscalls:sys_exit_nanosleep { printf("SUCCESS %d\n", pid); exit(); } EXPECT kprobe probe type requires 1 argument TIMEOUT 5 +WILL_FAIL NAME kprobe_order RUN {{BPFTRACE}} runtime/scripts/kprobe_order.bt @@ -22,35 +31,49 @@ AFTER /bin/bash -c "./testprogs/syscall nanosleep 1000"; /bin/bash -c "./testprogs/syscall nanosleep 1000" NAME kprobe_offset -PROG kprobe:vfs_read+0 { printf("SUCCESS '$test' %d\n", pid); exit(); } -EXPECT SUCCESS kprobe_offset [0-9][0-9]* +PROG kprobe:vfs_read+0 { printf("SUCCESS %d\n", pid); exit(); } +EXPECT SUCCESS [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read +NAME kprobe_wildcard +RUN {{BPFTRACE}} --unsafe -e 'kprobe:ksys_* { printf("progs: "); system("/usr/sbin/bpftool prog | grep kprobe | grep ksys_ | wc -l"); exit(); }' +EXPECT progs: 1 +TIMEOUT 5 +REQUIRES /usr/sbin/bpftool + +NAME kprobe_wildcard probe builtin +RUN {{BPFTRACE}} --unsafe -e 'kprobe:ksys_* { @ = probe; printf("progs: "); system("/usr/sbin/bpftool prog | grep kprobe | grep ksys_ | wc -l"); exit(); }' +EXPECT progs: [1-9][0-9]+ +TIMEOUT 5 +REQUIRES /usr/sbin/bpftool + # Note: this test may fail if you've installed a new kernel but not rebooted # yet. Reason is b/c bpftrace will look for a vmlinux based on the running kernel's # version. NAME kprobe_offset_fail_size -PROG kprobe:vfs_read+1000000 { printf("SUCCESS '$test' %d\n", pid); exit(); } +PROG kprobe:vfs_read+1000000 { printf("SUCCESS %d\n", pid); exit(); } EXPECT Offset outside the function bounds \('vfs_read' size is* TIMEOUT 5 +WILL_FAIL NAME kretprobe -PROG kretprobe:vfs_read { printf("SUCCESS '$test' %d\n", pid); exit(); } -EXPECT SUCCESS kretprobe [0-9][0-9]* +PROG kretprobe:vfs_read { printf("SUCCESS %d\n", pid); exit(); } +EXPECT SUCCESS [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kretprobe_short_name -PROG kr:vfs_read { printf("SUCCESS '$test' %d\n", pid); exit(); } -EXPECT SUCCESS kretprobe_short_name [0-9][0-9]* +PROG kr:vfs_read { printf("SUCCESS %d\n", pid); exit(); } +EXPECT SUCCESS [0-9][0-9]* TIMEOUT 5 AFTER ./testprogs/syscall read NAME kretprobe_target -PROG kretprobe:syscalls:sys_exit_nanosleep { printf("SUCCESS '$test' %d\n", pid); exit(); } +PROG kretprobe:syscalls:sys_exit_nanosleep { printf("SUCCESS %d\n", pid); exit(); } EXPECT kretprobe probe type requires 1 argument TIMEOUT 5 +WILL_FAIL NAME kretprobe_order RUN {{BPFTRACE}} runtime/scripts/kretprobe_order.bt @@ -58,6 +81,12 @@ TIMEOUT 5 AFTER /bin/bash -c "./testprogs/syscall nanosleep 1000"; /bin/bash -c "./testprogs/syscall nanosleep 1000" +NAME kretprobe_wildcard +RUN {{BPFTRACE}} --unsafe -e 'kretprobe:ksys_* { system("/usr/sbin/bpftool prog | grep kprobe | grep ksys_ | wc -l"); exit(); }' +EXPECT 1 +TIMEOUT 5 +REQUIRES /usr/sbin/bpftool + NAME uprobe PROG uprobe:/bin/bash:echo_builtin { printf("arg0: %d\n", arg0); exit();} EXPECT arg0: [0-9]* @@ -80,11 +109,13 @@ PROG uprobe:/bin/bash:echo_builtin+1000000 { printf("arg0: %d\n", arg0); exit();} EXPECT Offset outside the function bounds \('echo_builtin' size is* TIMEOUT 5 +WILL_FAIL NAME uprobe_address_fail_resolve PROG uprobe:/bin/bash:10 { printf("arg0: %d\n", arg0); exit();} EXPECT Could not resolve address: /bin/bash:0xa TIMEOUT 5 +WILL_FAIL NAME uprobe_library PROG uprobe:libc:fread { printf("size: %d\n", arg1); exit(); } @@ -112,14 +143,14 @@ AFTER /bin/bash -c "echo lala"; /bin/bash -c "echo lala" NAME tracepoint -PROG tracepoint:syscalls:sys_exit_nanosleep { printf("SUCCESS '$test' %d\n", gid); exit(); } -EXPECT SUCCESS tracepoint [0-9][0-9]* +PROG tracepoint:syscalls:sys_exit_nanosleep { printf("SUCCESS %d\n", gid); exit(); } +EXPECT SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall nanosleep 1e8 TIMEOUT 5 NAME tracepoint_short_name -PROG t:syscalls:sys_exit_nanosleep { printf("SUCCESS '$test' %d\n", gid); exit(); } -EXPECT SUCCESS tracepoint_short_name [0-9][0-9]* +PROG t:syscalls:sys_exit_nanosleep { printf("SUCCESS %d\n", gid); exit(); } +EXPECT SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall nanosleep 1e8 TIMEOUT 5 @@ -134,6 +165,19 @@ EXPECT hit hit TIMEOUT 5 +NAME tracepoint_missing +PROG t:syscalls:nonsense { printf("hit"); exit(); } +EXPECT ERROR: tracepoint not found: syscalls:nonsense +TIMEOUT 5 +WILL_FAIL + +NAME tracepoint_multiattach_missing +PROG t:syscalls:sys_exit_nanosleep,t:syscalls:nonsense { printf("hit"); exit(); } +EXPECT hit +AFTER ./testprogs/syscall nanosleep 1e8 +TIMEOUT 5 +WILL_FAIL + # Test that we get at least two characters out NAME tracepoint_data_loc PROG tracepoint:irq:irq_handler_entry { print(str(args->name)); exit(); } @@ -175,12 +219,21 @@ PROG hardware:cache-misses:10 { @[pid] = count(); exit(); } EXPECT @\[.*\]\:\s[0-9]* TIMEOUT 5 + NAME BEGIN PROG BEGIN { printf("Hello\n"); exit();} EXPECT Hello TIMEOUT 2 NAME END_processing_after_exit -RUN {{BPFTRACE}} -e "interval:s:1 { exit(); } END { printf("end"); }" +PROG interval:s:1 { exit(); } END { printf("end"); } EXPECT end TIMEOUT 2 + +# The "probe" builtin forces bpftrace to generate one BPF function for each +# match and so we should fail on exceeding BPFTRACE_MAX_BPF_PROGS. +NAME bpf_programs_limit +PROG k:* { @[probe] = count(); } +EXPECT BPFTRACE_MAX_BPF_PROGS +WILL_FAIL +TIMEOUT 2 diff -Nru bpftrace-0.14.1/tests/runtime/regression bpftrace-0.15.0/tests/runtime/regression --- bpftrace-0.14.1/tests/runtime/regression 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/regression 2022-05-24 10:41:38.000000000 +0000 @@ -1,7 +1,7 @@ # https://github.com/iovisor/bpftrace/pull/566#issuecomment-487295113 NAME codegen_struct_save_nested -PROG struct Foo { int m; struct { int x; int y; } bar; int n; } BEGIN { @foo = (struct Foo)0; @bar = @foo.bar; @x = @foo.bar.x; printf("done\n"); exit(); } -EXPECT done +PROG struct Foo { int m; struct { int x; int y; } bar; int n; } BEGIN { @foo = (struct Foo *)0; @bar = @foo->bar; @x = @foo->bar.x; printf("_%s_\n", "done"); exit(); } +EXPECT _done_ TIMEOUT 1 NAME logical_and_or_different_sizes @@ -66,7 +66,7 @@ TIMEOUT 3 NAME strncmp with prefix -PROG BEGIN { if (strncmp("hello", "hell") == 0) { printf("equal"); } else { printf("not equal"); } exit(); } +PROG BEGIN { if (strncmp("hello", "hell", 5) == 0) { printf("equal"); } else { printf("not equal"); } exit(); } EXPECT not equal TIMEOUT 3 @@ -76,3 +76,9 @@ PROG tracepoint:sched:sched_wakeup { if (args->comm == "asdf") { print(args->comm) } exit(); } EXPECT Attaching 1 probe TIMEOUT 1 + +# https://github.com/iovisor/bpftrace/issues/2135 +NAME address_probe_invalid_expansion +RUN {{BPFTRACE}} -e "uprobe:./testprogs/uprobe_test:0x$(nm ./testprogs/uprobe_test | awk '$3 == "function1" {print $1}') { @[probe] = count(); exit() }" +EXPECT Attaching 1 probe +TIMEOUT 1 diff -Nru bpftrace-0.14.1/tests/runtime/sigint bpftrace-0.15.0/tests/runtime/sigint --- bpftrace-0.14.1/tests/runtime/sigint 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/sigint 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -NAME strace no quit -RUN {{BPFTRACE}} -e 'i:s:1 { printf("%s %d\n", "SUCCESS", 1); exit() }' & (./testprogs/syscall nanosleep 5e8 && strace -p $! -o /dev/null) -EXPECT SUCCESS 1 -TIMEOUT 3 - -NAME sigint quit -RUN {{BPFTRACE}} -e 'END { printf("%s %d", "SUCCESS", 1) }' & (./testprogs/syscall nanosleep 1e9 && /usr/bin/env kill -s SIGINT $!) -EXPECT ^SUCCESS 1$ -TIMEOUT 2 diff -Nru bpftrace-0.14.1/tests/runtime/signals bpftrace-0.15.0/tests/runtime/signals --- bpftrace-0.14.1/tests/runtime/signals 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/signals 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,16 @@ +NAME strace no quit +RUN {{BPFTRACE}} -e 'i:s:1 { printf("%s %d\n", "SUCCESS", 1); exit() }' & (./testprogs/syscall nanosleep 5e8 && strace -p $! -o /dev/null) +EXPECT SUCCESS 1 +REQUIRES command -v strace +TIMEOUT 3 + +NAME sigint quit +RUN {{BPFTRACE}} -e 'END { printf("%s %d", "SUCCESS", 1) }' & (./testprogs/syscall nanosleep 1e9 && /usr/bin/env kill -s SIGINT $!) +EXPECT ^SUCCESS 1$ +TIMEOUT 2 + +NAME print all maps on sigusr1 +RUN {{BPFTRACE}} -f json -e 'BEGIN { @scalar = 5; } interval:s:2 { exit(); }' +AFTER kill -s USR1 $(pidof bpftrace) +EXPECT @scalar[\s\S]+@scalar +TIMEOUT 5 diff -Nru bpftrace-0.14.1/tests/runtime/tuples bpftrace-0.15.0/tests/runtime/tuples --- bpftrace-0.14.1/tests/runtime/tuples 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/tuples 2022-05-24 10:41:38.000000000 +0000 @@ -45,7 +45,7 @@ NAME complex tuple 4 PROG BEGIN { $a = ((int8)-100, (int8) 100, "abcdef", 3, (int32) 1, (int64)-10, (int8)10, (int16)-555, "abc"); print(sizeof($a)); exit(); } -EXPECT 168 +EXPECT 48 TIMEOUT 5 NAME struct in tuple diff -Nru bpftrace-0.14.1/tests/runtime/usdt bpftrace-0.15.0/tests/runtime/usdt --- bpftrace-0.14.1/tests/runtime/usdt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/usdt 2022-05-24 10:41:38.000000000 +0000 @@ -140,9 +140,13 @@ EXPECT Attaching 3 probes... TIMEOUT 5 +# Wasn't fully debugged, but looks like old bcc API (bcc_usdt_enable_fully_specified_probe) +# doesn't like inlined usdt probes very much. So that's why the bcc_usdt_addsem feature is +# required NAME "usdt probes - attach to probe on multiple files by wildcard" PROG usdt:./testprogs/usdt*::* { printf("here\n" ); exit(); } EXPECT Attaching 41 probes... +REQUIRES_FEATURE bcc_usdt_addsem TIMEOUT 5 NAME "usdt probes - attach to probe on multiple providers by wildcard and pid" @@ -215,6 +219,7 @@ TIMEOUT 5 REQUIRES ./testprogs/usdt_semaphore_test should_not_skip REQUIRES_FEATURE !uprobe_refcount +WILL_FAIL # We should be able to attach a probe even without any running processes # if the kernel handles the semaphore @@ -306,7 +311,7 @@ REQUIRES ./testprogs/usdt_inlined should_not_skip NAME "usdt probes in multiple modules" -RUN {{BPFTRACE}} runtime/scripts/usdt_multi_modules.bt -c ./testprogs/usdt_test +RUN {{BPFTRACE}} runtime/scripts/usdt_multi_modules.bt EXPECT Attaching 2 probes TIMEOUT 1 REQUIRES ./testprogs/usdt_test should_not_skip diff -Nru bpftrace-0.14.1/tests/runtime/variable bpftrace-0.15.0/tests/runtime/variable --- bpftrace-0.14.1/tests/runtime/variable 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/variable 2022-05-24 10:41:38.000000000 +0000 @@ -49,9 +49,41 @@ PROG tracepoint:random:urandom_read { $i = args->got_bits; printf("bits: %d\n", $i); if ($i == 24) { exit() } } EXPECT bits: 24 TIMEOUT 5 +MAX_KERNEL 5.16 AFTER dd if=/dev/urandom bs=3 count=1 +NAME 32-bit tracepoint arg openat_flags +PROG tracepoint:syscalls:sys_enter_openat /comm == "syscall"/ { $i = args->flags; printf("openflags: %d\n", $i); if ($i == 64) { exit() } } +EXPECT openflags: 64 +TIMEOUT 5 +AFTER ./testprogs/syscall openat + NAME tracepoint arg casts in predicates RUN {{BPFTRACE}} -e 'tracepoint:syscalls:sys_enter_wait4 /args->ru/ { @ru[tid] = args->ru; } tracepoint:syscalls:sys_exit_wait4 /@ru[tid]/ { @++; exit(); }' -c ./testprogs/wait4_ru EXPECT @: 1 TIMEOUT 5 + +NAME variable string type resize +PROG BEGIN { $x = "hello"; $x = "hi"; printf("%s\n", $x); exit(); } +EXPECT hi +TIMEOUT 2 + +NAME map string type resize +PROG BEGIN { @ = "hello"; } i:ms:1 { @ = "hi"; exit(); } +EXPECT @: hi +TIMEOUT 2 + +NAME map key string type resize +PROG BEGIN { @["hello"] = 0; } i:ms:1 { @["hi"] = 1; exit(); } +EXPECT @\[hi\]: 1 +TIMEOUT 2 + +NAME map multi-key string type resize +PROG BEGIN { @["hello", 0] = 0; } i:ms:1 { @["hi", 1] = 1; exit(); } +EXPECT @\[hi, 1\]: 1 +TIMEOUT 2 + +NAME tuple string resize +PROG BEGIN { @ = ("hello", 0); } i:ms:1 { @ = ("hi", 1); exit(); } +EXPECT @: \(hi, 1\) +TIMEOUT 2 diff -Nru bpftrace-0.14.1/tests/runtime/watchpoint bpftrace-0.15.0/tests/runtime/watchpoint --- bpftrace-0.14.1/tests/runtime/watchpoint 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/runtime/watchpoint 2022-05-24 10:41:38.000000000 +0000 @@ -1,5 +1,5 @@ NAME watchpoint - absolute address -RUN {{BPFTRACE}} -e 'watchpoint::0x10000000:8:w { printf("hit!\n"); exit() }' -c ./testprogs/watchpoint +RUN {{BPFTRACE}} -e 'watchpoint:0x10000000:8:w { printf("hit!\n"); exit() }' -c ./testprogs/watchpoint EXPECT hit! ARCH aarch64|ppc64|ppc64le|x86_64 TIMEOUT 5 diff -Nru bpftrace-0.14.1/tests/semantic_analyser.cpp bpftrace-0.15.0/tests/semantic_analyser.cpp --- bpftrace-0.14.1/tests/semantic_analyser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/semantic_analyser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -27,13 +27,13 @@ ASSERT_EQ(driver.parse_str(input), 0); ClangParser clang; - clang.parse(driver.root_, bpftrace); + clang.parse(driver.root.get(), bpftrace); ASSERT_EQ(driver.parse_str(input), 0); std::stringstream out; // Override to mockbpffeature. bpftrace.feature_ = std::make_unique(true); - ast::SemanticAnalyser semantics(driver.root_, bpftrace, out); + ast::SemanticAnalyser semantics(driver.root.get(), bpftrace, out); semantics.analyse(); if (invert) EXPECT_THAT(out.str(), Not(HasSubstr(warning))); @@ -72,17 +72,17 @@ if (expected_parse) return; - ast::FieldAnalyser fields(driver.root_, bpftrace, out); + ast::FieldAnalyser fields(driver.root.get(), bpftrace, out); EXPECT_EQ(fields.analyse(), expected_field_analyser) << msg.str() + out.str(); ClangParser clang; - clang.parse(driver.root_, bpftrace); + clang.parse(driver.root.get(), bpftrace); ASSERT_EQ(driver.parse_str(input), 0); out.str(""); // Override to mockbpffeature. bpftrace.feature_ = std::make_unique(mock_has_features); - ast::SemanticAnalyser semantics(driver.root_, bpftrace, out, has_child); + ast::SemanticAnalyser semantics(driver.root.get(), bpftrace, out, has_child); EXPECT_EQ(expected_result, semantics.analyse()) << msg.str() + out.str(); } @@ -234,15 +234,15 @@ test("kprobe:f { @x = 0; @x; }", 0); test("kprobe:f { @x[1] = 0; @x[2]; }", 0); - test("kprobe:f { @x = 0; @x[1]; }", 10); - test("kprobe:f { @x[1] = 0; @x; }", 10); + test("kprobe:f { @x = 0; @x[1]; }", 2); + test("kprobe:f { @x[1] = 0; @x; }", 2); test("kprobe:f { @x[1,2] = 0; @x[3,4]; }", 0); - test("kprobe:f { @x[1,2] = 0; @x[3]; }", 10); - test("kprobe:f { @x[1] = 0; @x[2,3]; }", 10); + test("kprobe:f { @x[1,2] = 0; @x[3]; }", 2); + test("kprobe:f { @x[1] = 0; @x[2,3]; }", 2); test("kprobe:f { @x[1,\"a\",kstack] = 0; @x[2,\"b\", kstack]; }", 0); - test("kprobe:f { @x[1,\"a\",kstack] = 0; @x[\"b\", 2, kstack]; }", 10); + test("kprobe:f { @x[1,\"a\",kstack] = 0; @x[\"b\", 2, kstack]; }", 2); } TEST(semantic_analyser, if_statements) @@ -740,7 +740,7 @@ for (size_t i = 0; i < sizes.size(); i++) { auto v = static_cast( - driver.root_->probes->at(0)->stmts->at(i)); + driver.root->probes->at(0)->stmts->at(i)); EXPECT_TRUE(v->var->type.IsPtrTy()); EXPECT_TRUE(v->var->type.GetPointeeTy()->IsIntTy()); EXPECT_EQ((unsigned long int)sizes.at(i), @@ -868,6 +868,39 @@ test("kprobe:f { macaddr(\"hello\"); }", 1); } +TEST(semantic_analyser, call_bswap) +{ + test("kprobe:f { bswap(arg0); }", 0); + + test("kprobe:f { bswap(0x12); }", 0); + test("kprobe:f { bswap(0x12 + 0x34); }", 0); + + test("kprobe:f { bswap((int8)0x12); }", 0); + test("kprobe:f { bswap((int16)0x12); }", 0); + test("kprobe:f { bswap((int32)0x12); }", 0); + test("kprobe:f { bswap((int64)0x12); }", 0); + + test("kprobe:f { bswap(); }", 1); + test("kprobe:f { bswap(0x12, 0x34); }", 1); + + test("kprobe:f { bswap(\"hello\"); }", 1); +} + +TEST(semantic_analyser, call_cgroup_path) +{ + test("kprobe:f { cgroup_path(1) }", 0); + test("kprobe:f { cgroup_path(1, \"hello\") }", 0); + + test("kprobe:f { cgroup_path(1, 2) }", 10); + test("kprobe:f { cgroup_path(\"1\") }", 10); + + test("kprobe:f { printf(\"%s\", cgroup_path(1)) }", 0); + test("kprobe:f { printf(\"%s %s\", cgroup_path(1), cgroup_path(2)) }", 0); + test("kprobe:f { $var = cgroup_path(0); printf(\"%s %s\", $var, $var) }", 0); + + test("kprobe:f { printf(\"%d\", cgroup_path(1)) }", 10); +} + TEST(semantic_analyser, map_reassignment) { test("kprobe:f { @x = 1; @x = 2; }", 0); @@ -928,7 +961,7 @@ "arg0; @x = $s->y[0];}", 0); auto assignment = static_cast( - driver.root_->probes->at(0)->stmts->at(1)); + driver.root->probes->at(0)->stmts->at(1)); EXPECT_EQ(CreateInt64(), assignment->map->type); test(driver, @@ -936,7 +969,7 @@ "arg0)->y; @x = $s[0];}", 0); auto array_var_assignment = static_cast( - driver.root_->probes->at(0)->stmts->at(0)); + driver.root->probes->at(0)->stmts->at(0)); EXPECT_EQ(CreateArray(4, CreateInt32()), array_var_assignment->var->type); test(driver, @@ -944,12 +977,12 @@ "arg0)->y; @x = @a[0][0];}", 0); auto array_map_assignment = static_cast( - driver.root_->probes->at(0)->stmts->at(0)); + driver.root->probes->at(0)->stmts->at(0)); EXPECT_EQ(CreateArray(4, CreateInt32()), array_map_assignment->map->type); test(driver, "kprobe:f { $s = (int32 *) arg0; $x = $s[0]; }", 0); auto var_assignment = static_cast( - driver.root_->probes->at(0)->stmts->at(1)); + driver.root->probes->at(0)->stmts->at(1)); EXPECT_EQ(CreateInt32(), var_assignment->var->type); // Positional parameter as index @@ -1006,7 +1039,7 @@ " @x[((struct MyStruct *)arg0)->x] = 0; " " @x[((struct MyStruct *)arg0)->y] = 1; " "}", - 10); + 2); } TEST(semantic_analyser, variable_type) @@ -1015,7 +1048,8 @@ Driver driver(bpftrace); test(driver, "kprobe:f { $x = 1 }", 0); auto st = CreateInt64(); - auto assignment = static_cast(driver.root_->probes->at(0)->stmts->at(0)); + auto assignment = static_cast( + driver.root->probes->at(0)->stmts->at(0)); EXPECT_EQ(st, assignment->var->type); } @@ -1043,8 +1077,10 @@ Driver driver(bpftrace); test(driver, "kprobe:f { $x = (int32) -1; @x = $x; }", 0); - auto var_assignment = static_cast(driver.root_->probes->at(0)->stmts->at(0)); - auto map_assignment = static_cast(driver.root_->probes->at(0)->stmts->at(1)); + auto var_assignment = static_cast( + driver.root->probes->at(0)->stmts->at(0)); + auto map_assignment = static_cast( + driver.root->probes->at(0)->stmts->at(1)); EXPECT_EQ(CreateInt32(), var_assignment->var->type); EXPECT_EQ(CreateInt64(), map_assignment->map->type); } @@ -1072,7 +1108,7 @@ test("kprobe:f { !(int32)0; }", 0); test("struct X { int n; } kprobe:f { $x = (struct X*)0; !$x; }", 10); test("struct X { int n; } kprobe:f { $x = *(struct X*)0; !$x; }", 10); - test("kprobe:f { !\"0\"; }", 1); + test("kprobe:f { !\"0\"; }", 10); } TEST(semantic_analyser, unop_increment_decrement) @@ -1135,6 +1171,7 @@ test("kprobe:f { $x = 123; printf(\"int: %d\", $x) }", 0); test("kprobe:f { printf(\"int: %u\", 1234) }", 0); + test("kprobe:f { printf(\"int: %o\", 1234) }", 0); test("kprobe:f { printf(\"int: %x\", 1234) }", 0); test("kprobe:f { printf(\"int: %X\", 1234) }", 0); } @@ -1143,48 +1180,56 @@ { test("kprobe:f { printf(\"int: %d\", 1234) }", 0); test("kprobe:f { printf(\"int: %u\", 1234) }", 0); + test("kprobe:f { printf(\"int: %o\", 1234) }", 0); test("kprobe:f { printf(\"int: %x\", 1234) }", 0); test("kprobe:f { printf(\"int: %X\", 1234) }", 0); test("kprobe:f { printf(\"int: %p\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhd\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhu\", 1234) }", 0); + test("kprobe:f { printf(\"int: %hho\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhx\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhX\", 1234) }", 0); test("kprobe:f { printf(\"int: %hhp\", 1234) }", 0); test("kprobe:f { printf(\"int: %hd\", 1234) }", 0); test("kprobe:f { printf(\"int: %hu\", 1234) }", 0); + test("kprobe:f { printf(\"int: %ho\", 1234) }", 0); test("kprobe:f { printf(\"int: %hx\", 1234) }", 0); test("kprobe:f { printf(\"int: %hX\", 1234) }", 0); test("kprobe:f { printf(\"int: %hp\", 1234) }", 0); test("kprobe:f { printf(\"int: %ld\", 1234) }", 0); test("kprobe:f { printf(\"int: %lu\", 1234) }", 0); + test("kprobe:f { printf(\"int: %lo\", 1234) }", 0); test("kprobe:f { printf(\"int: %lx\", 1234) }", 0); test("kprobe:f { printf(\"int: %lX\", 1234) }", 0); test("kprobe:f { printf(\"int: %lp\", 1234) }", 0); test("kprobe:f { printf(\"int: %lld\", 1234) }", 0); test("kprobe:f { printf(\"int: %llu\", 1234) }", 0); + test("kprobe:f { printf(\"int: %llo\", 1234) }", 0); test("kprobe:f { printf(\"int: %llx\", 1234) }", 0); test("kprobe:f { printf(\"int: %llX\", 1234) }", 0); test("kprobe:f { printf(\"int: %llp\", 1234) }", 0); test("kprobe:f { printf(\"int: %jd\", 1234) }", 0); test("kprobe:f { printf(\"int: %ju\", 1234) }", 0); + test("kprobe:f { printf(\"int: %jo\", 1234) }", 0); test("kprobe:f { printf(\"int: %jx\", 1234) }", 0); test("kprobe:f { printf(\"int: %jX\", 1234) }", 0); test("kprobe:f { printf(\"int: %jp\", 1234) }", 0); test("kprobe:f { printf(\"int: %zd\", 1234) }", 0); test("kprobe:f { printf(\"int: %zu\", 1234) }", 0); + test("kprobe:f { printf(\"int: %zo\", 1234) }", 0); test("kprobe:f { printf(\"int: %zx\", 1234) }", 0); test("kprobe:f { printf(\"int: %zX\", 1234) }", 0); test("kprobe:f { printf(\"int: %zp\", 1234) }", 0); test("kprobe:f { printf(\"int: %td\", 1234) }", 0); test("kprobe:f { printf(\"int: %tu\", 1234) }", 0); + test("kprobe:f { printf(\"int: %to\", 1234) }", 0); test("kprobe:f { printf(\"int: %tx\", 1234) }", 0); test("kprobe:f { printf(\"int: %tX\", 1234) }", 0); test("kprobe:f { printf(\"int: %tp\", 1234) }", 0); @@ -1219,6 +1264,17 @@ test("kprobe:f { printf(\"%r\", arg0) }", 10); } +TEST(semantic_analyser, printf_format_buf_no_ascii) +{ + test("kprobe:f { printf(\"%rx\", buf(\"mystr\", 5)) }", 0); +} + +TEST(semantic_analyser, printf_bad_format_buf_no_ascii) +{ + test("kprobe:f { printf(\"%rx\", \"mystr\") }", 10); + test("kprobe:f { printf(\"%rx\", arg0) }", 10); +} + TEST(semantic_analyser, printf_format_multi) { test("kprobe:f { printf(\"%d %d %s\", 1, 2, \"mystr\") }", 0); @@ -1524,7 +1580,7 @@ { test(driver, structs + "kprobe:f { $x = (*(struct type1*)0).x }", 0); - auto stmts = driver.root_->probes->at(0)->stmts; + auto stmts = driver.root->probes->at(0)->stmts; auto var_assignment1 = static_cast(stmts->at(0)); EXPECT_FALSE(var_assignment1->var->type.is_internal); } @@ -1533,7 +1589,7 @@ test(driver, structs + "kprobe:f { @type1 = *(struct type1*)0; $x = @type1.x }", 0); - auto stmts = driver.root_->probes->at(0)->stmts; + auto stmts = driver.root->probes->at(0)->stmts; auto map_assignment = static_cast(stmts->at(0)); auto var_assignment2 = static_cast(stmts->at(1)); EXPECT_TRUE(map_assignment->map->type.is_internal); @@ -1557,7 +1613,7 @@ " @x[*((struct A *)arg0)] = 0; " " @x[*((struct B *)arg1)] = 1; " "}", - 10); + 2); } TEST(semantic_analyser, probe_short_name) @@ -1611,7 +1667,7 @@ Driver driver(bpftrace); test(driver, "k:f { $1 }", 0); auto stmt = static_cast( - driver.root_->probes->at(0)->stmts->at(0)); + driver.root->probes->at(0)->stmts->at(0)); auto pp = static_cast(stmt->expr); EXPECT_EQ(CreateInt64(), pp->type); EXPECT_TRUE(pp->is_literal); @@ -1739,10 +1795,14 @@ " $s = $t->s; $us = $t->us; $l = $t->l; $lu = $t->ul; }"; test(driver, prog, 0); - auto s = static_cast(driver.root_->probes->at(0)->stmts->at(1)); - auto us = static_cast(driver.root_->probes->at(0)->stmts->at(2)); - auto l = static_cast(driver.root_->probes->at(0)->stmts->at(3)); - auto ul = static_cast(driver.root_->probes->at(0)->stmts->at(4)); + auto s = static_cast( + driver.root->probes->at(0)->stmts->at(1)); + auto us = static_cast( + driver.root->probes->at(0)->stmts->at(2)); + auto l = static_cast( + driver.root->probes->at(0)->stmts->at(3)); + auto ul = static_cast( + driver.root->probes->at(0)->stmts->at(4)); EXPECT_EQ(CreateInt32(), s->var->type); EXPECT_EQ(CreateUInt32(), us->var->type); EXPECT_EQ(CreateInt64(), l->var->type); @@ -1769,11 +1829,14 @@ "}"; test(driver, prog, 0); - auto varA = static_cast(driver.root_->probes->at(0)->stmts->at(1)); + auto varA = static_cast( + driver.root->probes->at(0)->stmts->at(1)); EXPECT_EQ(CreateInt64(), varA->var->type); - auto varB = static_cast(driver.root_->probes->at(0)->stmts->at(2)); + auto varB = static_cast( + driver.root->probes->at(0)->stmts->at(2)); EXPECT_EQ(CreateUInt64(), varB->var->type); - auto varC = static_cast(driver.root_->probes->at(0)->stmts->at(3)); + auto varC = static_cast( + driver.root->probes->at(0)->stmts->at(3)); EXPECT_EQ(CreateUInt64(), varC->var->type); } } @@ -2001,7 +2064,7 @@ structs + "kprobe:f { $x = (struct x*)ctx; $a = $x->a; $b = $x->b[0]; " "$c = $x->c.c; $d = $x->d->c;}", 0); - auto &stmts = driver.root_->probes->at(0)->stmts; + auto &stmts = driver.root->probes->at(0)->stmts; // $x = (struct x*)ctx; auto assignment = static_cast(stmts->at(0)); @@ -2080,7 +2143,7 @@ BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $pp = (int8 **)1; $p = *$pp; $val = *$p; }", 0); - auto &stmts = driver.root_->probes->at(0)->stmts; + auto &stmts = driver.root->probes->at(0)->stmts; // $pp = (int8 **)1; auto assignment = static_cast(stmts->at(0)); @@ -2111,7 +2174,7 @@ "struct Foo { char x; long y; }" "kprobe:f { $pp = (struct Foo **)1; $p = *$pp; $val = $p->x; }", 0); - auto &stmts = driver.root_->probes->at(0)->stmts; + auto &stmts = driver.root->probes->at(0)->stmts; // $pp = (struct Foo **)1; auto assignment = static_cast(stmts->at(0)); @@ -2243,7 +2306,7 @@ test(R"_(BEGIN { $t = ((int32)1, (int64)2); $t = ((int64)1, (int32)2); })_", 10); - test(R"_(BEGIN { @t = (1, 2); @t = (4, "other"); })_", 1); + test(R"_(BEGIN { @t = (1, 2); @t = (4, "other"); })_", 10); test(R"_(BEGIN { @t = (1, 2); @t = 5; })_", 1); test(R"_(BEGIN { @t = (1, count()) })_", 1); test(R"_(BEGIN { @t = (1, (aaa)0) })_", 1); @@ -2268,14 +2331,14 @@ BPFtrace bpftrace; Driver driver(bpftrace); SizedType ty = CreateTuple( - bpftrace.structs.AddTuple({ CreateInt64(), CreateString(64) })); + bpftrace.structs.AddTuple({ CreateInt64(), CreateString(6) })); test(bpftrace, true, driver, R"_(BEGIN { $t = (1, "str"); $t = (4, "other"); })_", 0); - auto &stmts = driver.root_->probes->at(0)->stmts; + auto &stmts = driver.root->probes->at(0)->stmts; // $t = (1, "str"); auto assignment = static_cast(stmts->at(0)); @@ -2298,7 +2361,7 @@ R"_(BEGIN { @ = (1, 3, 3, 7); @ = (0, 0, 0, 0); })_", 0); - auto &stmts = driver.root_->probes->at(0)->stmts; + auto &stmts = driver.root->probes->at(0)->stmts; // $t = (1, 3, 3, 7); auto assignment = static_cast(stmts->at(0)); @@ -2324,7 +2387,7 @@ bpftrace.structs.AddTuple({ CreateInt64(), ty_inner })); test(bpftrace, true, driver, R"_(BEGIN { $t = (1,(1,2)); })_", 0); - auto &stmts = driver.root_->probes->at(0)->stmts; + auto &stmts = driver.root->probes->at(0)->stmts; // $t = (1, "str"); auto assignment = static_cast(stmts->at(0)); @@ -2334,7 +2397,7 @@ TEST(semantic_analyser, tuple_types_unique) { auto bpftrace = get_mock_bpftrace(); - test(*bpftrace, R"_(BEGIN { $t = (1, "str"); $t = (4, "other"); })_", 0); + test(*bpftrace, R"_(BEGIN { $t = (1, "hello"); $t = (4, "other"); })_", 0); EXPECT_EQ(bpftrace->structs.GetTuplesCnt(), 1ul); } @@ -2389,6 +2452,57 @@ test("tracepoint:file:filename { args->common_field }", 1); } +TEST(semantic_analyser, string_size) +{ + // Size of the variable should be the size of the larger string (incl. null) + BPFtrace bpftrace; + Driver driver(bpftrace); + test(bpftrace, true, driver, R"_(BEGIN { $x = "hi"; $x = "hello"; })_", 0); + auto stmt = driver.root->probes->at(0)->stmts->at(0); + auto var_assign = dynamic_cast(stmt); + ASSERT_TRUE(var_assign->var->type.IsStringTy()); + ASSERT_EQ(var_assign->var->type.GetSize(), 6UL); + + test(bpftrace, true, driver, R"_(k:f1 {@ = "hi";} k:f2 {@ = "hello";})_", 0); + stmt = driver.root->probes->at(0)->stmts->at(0); + auto map_assign = dynamic_cast(stmt); + ASSERT_TRUE(map_assign->map->type.IsStringTy()); + ASSERT_EQ(map_assign->map->type.GetSize(), 6UL); + + test(bpftrace, + true, + driver, + R"_(k:f1 {@["hi"] = 0;} k:f2 {@["hello"] = 1;})_", + 0); + stmt = driver.root->probes->at(0)->stmts->at(0); + map_assign = dynamic_cast(stmt); + ASSERT_TRUE(map_assign->map->key_type.args_.at(0).IsStringTy()); + ASSERT_EQ(map_assign->map->key_type.args_.at(0).GetSize(), 6UL); + + test(bpftrace, + true, + driver, + R"_(k:f1 {@["hi", 0] = 0;} k:f2 {@["hello", 1] = 1;})_", + 0); + stmt = driver.root->probes->at(0)->stmts->at(0); + map_assign = dynamic_cast(stmt); + ASSERT_EQ(map_assign->map->key_type.size(), 14UL); + ASSERT_TRUE(map_assign->map->key_type.args_.at(0).IsStringTy()); + ASSERT_EQ(map_assign->map->key_type.args_.at(0).GetSize(), 6UL); + + test(bpftrace, + true, + driver, + R"_(k:f1 {$x = ("hello", 0);} k:f2 {$x = ("hi", 0); })_", + 0); + stmt = driver.root->probes->at(0)->stmts->at(0); + var_assign = dynamic_cast(stmt); + ASSERT_TRUE(var_assign->var->type.IsTupleTy()); + ASSERT_TRUE(var_assign->var->type.GetField(0).type.IsStringTy()); + ASSERT_EQ(var_assign->var->type.GetSize(), 16UL); // tuples are not packed + ASSERT_EQ(var_assign->var->type.GetField(0).type.GetSize(), 6UL); +} + #ifdef HAVE_LIBBPF_BTF_DUMP #include "btf_common.h" @@ -2513,6 +2627,35 @@ 1); } +TEST_F(semantic_analyser_dwarf, parse_struct) +{ + BPFtrace bpftrace; + Driver driver(bpftrace); + + std::string uprobe = "uprobe:" + std::string(bin_); + test(bpftrace, false, driver, uprobe + ":func_1 { $x = args->foo1->a; }", 0); + ASSERT_TRUE(bpftrace.structs.Has("struct Foo1")); + auto str = bpftrace.structs.Lookup("struct Foo1").lock(); + + ASSERT_TRUE(str->HasFields()); + ASSERT_EQ(str->fields.size(), 3); + ASSERT_EQ(str->size, 16); + + ASSERT_TRUE(str->HasField("a")); + ASSERT_TRUE(str->GetField("a").type.IsIntTy()); + ASSERT_EQ(str->GetField("a").type.GetSize(), 4); + ASSERT_EQ(str->GetField("a").offset, 0); + + ASSERT_TRUE(str->HasField("b")); + ASSERT_TRUE(str->GetField("b").type.IsIntTy()); + ASSERT_EQ(str->GetField("b").type.GetSize(), 1); + ASSERT_EQ(str->GetField("b").offset, 4); + + ASSERT_TRUE(str->HasField("c")); + ASSERT_TRUE(str->GetField("c").type.IsIntTy()); + ASSERT_EQ(str->GetField("c").type.GetSize(), 8); +} + #endif // HAVE_LIBDW } // namespace semantic_analyser diff -Nru bpftrace-0.14.1/tests/testprogs/uprobe_test.c bpftrace-0.15.0/tests/testprogs/uprobe_test.c --- bpftrace-0.14.1/tests/testprogs/uprobe_test.c 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/testprogs/uprobe_test.c 2022-05-24 10:41:38.000000000 +0000 @@ -9,6 +9,7 @@ { int a; char b[10]; + int c[3]; }; int function1(int *n, char c __attribute__((unused))) @@ -29,8 +30,8 @@ char c = 'x'; function1(&n, c); - struct Foo foo1 = { .a = 123, .b = "hello" }; - struct Foo foo2 = { .a = 456, .b = "world" }; + struct Foo foo1 = { .a = 123, .b = "hello", .c = { 1, 2, 3 } }; + struct Foo foo2 = { .a = 456, .b = "world", .c = { 4, 5, 6 } }; function2(&foo1, &foo2); return 0; diff -Nru bpftrace-0.14.1/tests/tools-parsing-test.sh bpftrace-0.15.0/tests/tools-parsing-test.sh --- bpftrace-0.14.1/tests/tools-parsing-test.sh 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/tools-parsing-test.sh 2022-05-24 10:41:38.000000000 +0000 @@ -1,5 +1,6 @@ #!/bin/bash +set -u set +e; if [[ $EUID -ne 0 ]]; then @@ -8,35 +9,67 @@ fi DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" - BPFTRACE_EXECUTABLE=${BPFTRACE_EXECUTABLE:-$DIR/../src/bpftrace}; - EXIT_STATUS=0; - TOOLDIR="" +OLDTOOLS=${TOOLS_TEST_OLDVERSION:-} +IFS=',' read -ra SKIP_TOOLS <<< "${TOOLS_TEST_DISABLE:-"NONE"}" -function tooldir() { - for dir in "../../tools" "/vagrant/tools"; do - if [[ -d "$dir" ]]; then - TOOLDIR="$dir" - return - fi - done +function set_tooldir() { + local dir + for dir in "../../tools" "/vagrant/tools"; do + if [[ -d "$dir" ]]; then + TOOLDIR="$dir" + return + fi + done - >&2 echo "Tool dir not found" - exit 1 + >&2 echo "Tool dir not found" + exit 1 } -tooldir - -for f in "$TOOLDIR"/*.bt; do - if $BPFTRACE_EXECUTABLE --unsafe -d $f 2>/dev/null >/dev/null; then - echo "$f passed" +function do_test() { + local file="$1" + if $BPFTRACE_EXECUTABLE --unsafe -d "$file" 2>/dev/null >/dev/null; then + echo "$file passed" else - echo "$f failed"; - $BPFTRACE_EXECUTABLE --unsafe -d $f; + echo "$file failed"; + $BPFTRACE_EXECUTABLE --unsafe -d "$file"; EXIT_STATUS=1; fi -done +} + +function skip_test() { + local name + name="$(basename "$1")" + for i in "${SKIP_TOOLS[@]}"; do + if [[ "$i" == "$name" ]]; then + return 0 + fi + done + return 1 +} + + +function do_tests () { + local f + local tool + for f in "$TOOLDIR"/*.bt; do + if skip_test "$f"; then + echo "Skipping $f" + else + if [[ $OLDTOOLS =~ $(basename "$f") ]]; then + tool="$(dirname "$f")/old/$(basename "$f")" + do_test "$tool" + else + do_test "$f" + fi + fi + done +} + + +set_tooldir +do_tests exit $EXIT_STATUS diff -Nru bpftrace-0.14.1/tests/tracepoint_format_parser.cpp bpftrace-0.15.0/tests/tracepoint_format_parser.cpp --- bpftrace-0.14.1/tests/tracepoint_format_parser.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/tracepoint_format_parser.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -260,23 +260,23 @@ ast::TracepointArgsVisitor visitor; EXPECT_EQ(driver.parse_str("BEGIN { args->f1->f2->f3 }"), 0); - visitor.visit(*driver.root_->probes->at(0)); - EXPECT_EQ(driver.root_->probes->at(0)->tp_args_structs_level, 3); + visitor.visit(*driver.root->probes->at(0)); + EXPECT_EQ(driver.root->probes->at(0)->tp_args_structs_level, 3); // Should work via intermediary variable, too EXPECT_EQ(driver.parse_str("BEGIN { $x = args->f1; $x->f2->f3 }"), 0); - visitor.visit(*driver.root_->probes->at(0)); - EXPECT_EQ(driver.root_->probes->at(0)->tp_args_structs_level, 3); + visitor.visit(*driver.root->probes->at(0)); + EXPECT_EQ(driver.root->probes->at(0)->tp_args_structs_level, 3); // "args" used without field access => level should be 0 EXPECT_EQ(driver.parse_str("BEGIN { args }"), 0); - visitor.visit(*driver.root_->probes->at(0)); - EXPECT_EQ(driver.root_->probes->at(0)->tp_args_structs_level, 0); + visitor.visit(*driver.root->probes->at(0)); + EXPECT_EQ(driver.root->probes->at(0)->tp_args_structs_level, 0); // "args" not used => level should be -1 EXPECT_EQ(driver.parse_str("BEGIN { x->f1->f2->f3 }"), 0); - visitor.visit(*driver.root_->probes->at(0)); - EXPECT_EQ(driver.root_->probes->at(0)->tp_args_structs_level, -1); + visitor.visit(*driver.root->probes->at(0)); + EXPECT_EQ(driver.root->probes->at(0)->tp_args_structs_level, -1); } } // namespace tracepoint_format_parser diff -Nru bpftrace-0.14.1/tests/utils.cpp bpftrace-0.15.0/tests/utils.cpp --- bpftrace-0.14.1/tests/utils.cpp 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tests/utils.cpp 2022-05-24 10:41:38.000000000 +0000 @@ -3,10 +3,21 @@ #include "gtest/gtest.h" #include #include +#include #include #include #include +#if __has_include() +#include +namespace std_filesystem = std::filesystem; +#elif __has_include() +#include +namespace std_filesystem = std::experimental::filesystem; +#else +#error "neither nor are present" +#endif + namespace bpftrace { namespace test { namespace utils { @@ -158,6 +169,59 @@ remove(rel_file.c_str()); } +TEST(utils, get_cgroup_hierarchy_roots) +{ + auto roots = get_cgroup_hierarchy_roots(); + + // Check that each entry is a proper cgroup filesystem + for (auto root : roots) + { + EXPECT_TRUE(root.first == "cgroup" || root.first == "cgroup2"); + std_filesystem::path root_path(root.second); + EXPECT_TRUE(std_filesystem::exists(root_path / "cgroup.procs")); + } +} + +TEST(utils, get_cgroup_path_in_hierarchy) +{ + std::string tmpdir = "/tmp/bpftrace-test-utils-XXXXXX"; + + if (::mkdtemp(&tmpdir[0]) == nullptr) + { + throw std::runtime_error("creating temporary path for tests failed"); + } + + const std_filesystem::path path(tmpdir); + const std_filesystem::path file_1 = path / "file1"; + const std_filesystem::path subdir = path / "subdir"; + const std_filesystem::path file_2 = subdir / "file2"; + + // Make a few files in the directory to imitate cgroup files and get their + // inodes + if (!std_filesystem::create_directory(subdir)) + { + throw std::runtime_error("creating subdirectory for tests failed"); + } + static_cast(std::ofstream(file_1) << "File 1 content") + .close(); + static_cast(std::ofstream(file_2) << "File 2 content") + .close(); + struct stat file_1_st, file_2_st; + if (stat(file_1.c_str(), &file_1_st) < 0 || + stat(file_2.c_str(), &file_2_st) < 0) + { + throw std::runtime_error("stat on test files failed"); + } + + // Look for both "cgroup files" by their inode twice (to test caching) + for (int i = 0; i < 2; i++) + { + EXPECT_EQ(get_cgroup_path_in_hierarchy(file_1_st.st_ino, tmpdir), "/file1"); + EXPECT_EQ(get_cgroup_path_in_hierarchy(file_2_st.st_ino, tmpdir), + "/subdir/file2"); + } +} + } // namespace utils } // namespace test } // namespace bpftrace diff -Nru bpftrace-0.14.1/tools/biolatency.bt bpftrace-0.15.0/tools/biolatency.bt --- bpftrace-0.14.1/tools/biolatency.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/biolatency.bt 2022-05-24 10:41:38.000000000 +0000 @@ -16,12 +16,14 @@ printf("Tracing block device I/O... Hit Ctrl-C to end.\n"); } -kprobe:blk_account_io_start +kprobe:blk_account_io_start, +kprobe:__blk_account_io_start { @start[arg0] = nsecs; } -kprobe:blk_account_io_done +kprobe:blk_account_io_done, +kprobe:__blk_account_io_done /@start[arg0]/ { @usecs = hist((nsecs - @start[arg0]) / 1000); diff -Nru bpftrace-0.14.1/tools/biosnoop.bt bpftrace-0.15.0/tools/biosnoop.bt --- bpftrace-0.14.1/tools/biosnoop.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/biosnoop.bt 2022-05-24 10:41:38.000000000 +0000 @@ -1,5 +1,4 @@ #!/usr/bin/env bpftrace -#include /* * biosnoop.bt Block I/O tracing tool, showing per I/O latency. * For Linux, uses bpftrace, eBPF. @@ -11,20 +10,27 @@ * 15-Nov-2017 Brendan Gregg Created this. */ +#ifndef BPFTRACE_HAVE_BTF +#include +#include +#endif + BEGIN { printf("%-12s %-7s %-16s %-6s %7s\n", "TIME(ms)", "DISK", "COMM", "PID", "LAT(ms)"); } -kprobe:blk_account_io_start +kprobe:blk_account_io_start, +kprobe:__blk_account_io_start { @start[arg0] = nsecs; @iopid[arg0] = pid; @iocomm[arg0] = comm; - @disk[arg0] = ((struct request *)arg0)->rq_disk->disk_name; + @disk[arg0] = ((struct request *)arg0)->q->disk->disk_name; } -kprobe:blk_account_io_done +kprobe:blk_account_io_done, +kprobe:__blk_account_io_done /@start[arg0] != 0 && @iopid[arg0] != 0 && @iocomm[arg0] != ""/ { diff -Nru bpftrace-0.14.1/tools/biostacks.bt bpftrace-0.15.0/tools/biostacks.bt --- bpftrace-0.14.1/tools/biostacks.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/biostacks.bt 2022-05-24 10:41:38.000000000 +0000 @@ -18,7 +18,8 @@ printf("Tracing block I/O with init stacks. Hit Ctrl-C to end.\n"); } -kprobe:blk_account_io_start +kprobe:blk_account_io_start, +kprobe:__blk_account_io_start { @reqstack[arg0] = kstack; @reqts[arg0] = nsecs; diff -Nru bpftrace-0.14.1/tools/CMakeLists.txt bpftrace-0.15.0/tools/CMakeLists.txt --- bpftrace-0.14.1/tools/CMakeLists.txt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/CMakeLists.txt 2022-05-24 10:41:38.000000000 +0000 @@ -3,3 +3,4 @@ list(REMOVE_ITEM TXT_FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) install(FILES ${BT_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bpftrace/tools) install(FILES ${TXT_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bpftrace/tools/doc) +add_subdirectory(old) diff -Nru bpftrace-0.14.1/tools/dcsnoop.bt bpftrace-0.15.0/tools/dcsnoop.bt --- bpftrace-0.14.1/tools/dcsnoop.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/dcsnoop.bt 2022-05-24 10:41:38.000000000 +0000 @@ -15,6 +15,7 @@ * 08-Sep-2018 Brendan Gregg Created this. */ +#ifndef BPFTRACE_HAVE_BTF #include #include @@ -24,6 +25,7 @@ struct qstr last; // [...] }; +#endif BEGIN { diff -Nru bpftrace-0.14.1/tools/gethostlatency.bt bpftrace-0.15.0/tools/gethostlatency.bt --- bpftrace-0.14.1/tools/gethostlatency.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/gethostlatency.bt 2022-05-24 10:41:38.000000000 +0000 @@ -26,17 +26,17 @@ "HOST"); } -uprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo, -uprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname, -uprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname2 +uprobe:libc:getaddrinfo, +uprobe:libc:gethostbyname, +uprobe:libc:gethostbyname2 { @start[tid] = nsecs; @name[tid] = arg0; } -uretprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo, -uretprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname, -uretprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname2 +uretprobe:libc:getaddrinfo, +uretprobe:libc:gethostbyname, +uretprobe:libc:gethostbyname2 /@start[tid]/ { $latms = (nsecs - @start[tid]) / 1e6; diff -Nru bpftrace-0.14.1/tools/mdflush.bt bpftrace-0.15.0/tools/mdflush.bt --- bpftrace-0.14.1/tools/mdflush.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/mdflush.bt 2022-05-24 10:41:38.000000000 +0000 @@ -7,14 +7,18 @@ * * This is a bpftrace version of the bcc tool of the same name. * + * For Linux 5.12+ (see tools/old for script for lower versions). + * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ +#ifndef BPFTRACE_HAVE_BTF #include #include +#endif BEGIN { @@ -26,5 +30,5 @@ { time("%H:%M:%S "); printf("%-6d %-16s %s\n", pid, comm, - ((struct bio *)arg1)->bi_disk->disk_name); + ((struct bio *)arg1)->bi_bdev->bd_disk->disk_name); } diff -Nru bpftrace-0.14.1/tools/naptime.bt bpftrace-0.15.0/tools/naptime.bt --- bpftrace-0.14.1/tools/naptime.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/naptime.bt 2022-05-24 10:41:38.000000000 +0000 @@ -13,8 +13,10 @@ * 16-Feb-2019 Brendan Gregg Created this. */ +#ifndef BPFTRACE_HAVE_BTF #include #include +#endif BEGIN { diff -Nru bpftrace-0.14.1/tools/old/biosnoop.bt bpftrace-0.15.0/tools/old/biosnoop.bt --- bpftrace-0.14.1/tools/old/biosnoop.bt 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/tools/old/biosnoop.bt 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,56 @@ +#!/usr/bin/env bpftrace +/* + * biosnoop.bt Block I/O tracing tool, showing per I/O latency. + * For Linux, uses bpftrace, eBPF. + * + * TODO: switch to block tracepoints. Add offset and size columns. + * + * This is a bpftrace version of the bcc tool of the same name. + * + * For Linux <= 5.16. + * + * 15-Nov-2017 Brendan Gregg Created this. + */ + +#ifndef BPFTRACE_HAVE_BTF +#include +#include +#endif + +BEGIN +{ + printf("%-12s %-7s %-16s %-6s %7s\n", "TIME(ms)", "DISK", "COMM", "PID", "LAT(ms)"); +} + +kprobe:blk_account_io_start, +kprobe:__blk_account_io_start +{ + @start[arg0] = nsecs; + @iopid[arg0] = pid; + @iocomm[arg0] = comm; + @disk[arg0] = ((struct request *)arg0)->rq_disk->disk_name; +} + +kprobe:blk_account_io_done, +kprobe:__blk_account_io_done +/@start[arg0] != 0 && @iopid[arg0] != 0 && @iocomm[arg0] != ""/ + +{ + $now = nsecs; + printf("%-12u %-7s %-16s %-6d %7d\n", + elapsed / 1e6, @disk[arg0], @iocomm[arg0], @iopid[arg0], + ($now - @start[arg0]) / 1e6); + + delete(@start[arg0]); + delete(@iopid[arg0]); + delete(@iocomm[arg0]); + delete(@disk[arg0]); +} + +END +{ + clear(@start); + clear(@iopid); + clear(@iocomm); + clear(@disk); +} diff -Nru bpftrace-0.14.1/tools/old/CMakeLists.txt bpftrace-0.15.0/tools/old/CMakeLists.txt --- bpftrace-0.14.1/tools/old/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/tools/old/CMakeLists.txt 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,2 @@ +file(GLOB BT_FILES *.bt) +install(FILES ${BT_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bpftrace/tools/old) diff -Nru bpftrace-0.14.1/tools/old/mdflush.bt bpftrace-0.15.0/tools/old/mdflush.bt --- bpftrace-0.14.1/tools/old/mdflush.bt 1970-01-01 00:00:00.000000000 +0000 +++ bpftrace-0.15.0/tools/old/mdflush.bt 2022-05-24 10:41:38.000000000 +0000 @@ -0,0 +1,34 @@ +#!/usr/bin/env bpftrace +/* + * mdflush Trace md flush events. + * For Linux, uses bpftrace and eBPF. + * + * USAGE: mdflush.bt + * + * This is a bpftrace version of the bcc tool of the same name. + * + * For Linux <= 5.11. + * + * Copyright 2018 Netflix, Inc. + * Licensed under the Apache License, Version 2.0 (the "License") + * + * 08-Sep-2018 Brendan Gregg Created this. + */ + +#ifndef __BPFTRACE_HAVE_BTF +#include +#include +#endif + +BEGIN +{ + printf("Tracing md flush events... Hit Ctrl-C to end.\n"); + printf("%-8s %-6s %-16s %s", "TIME", "PID", "COMM", "DEVICE"); +} + +kprobe:md_flush_request +{ + time("%H:%M:%S "); + printf("%-6d %-16s %s\n", pid, comm, + ((struct bio *)arg1)->bi_disk->disk_name); +} diff -Nru bpftrace-0.14.1/tools/oomkill.bt bpftrace-0.15.0/tools/oomkill.bt --- bpftrace-0.14.1/tools/oomkill.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/oomkill.bt 2022-05-24 10:41:38.000000000 +0000 @@ -20,7 +20,9 @@ * 07-Sep-2018 Brendan Gregg Created this. */ +#ifndef BPFTRACE_HAVE_BTF #include +#endif BEGIN { diff -Nru bpftrace-0.14.1/tools/runqlat_example.txt bpftrace-0.15.0/tools/runqlat_example.txt --- bpftrace-0.14.1/tools/runqlat_example.txt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/runqlat_example.txt 2022-05-24 10:41:38.000000000 +0000 @@ -119,7 +119,7 @@ wait its turn on the one CPU. -Now I'l run 10 CPU-bound throuds on one CPU: +Now I'll run 10 CPU-bound threads on one CPU: # ./runqlat.bt Attaching 5 probes... diff -Nru bpftrace-0.14.1/tools/runqlen.bt bpftrace-0.15.0/tools/runqlen.bt --- bpftrace-0.14.1/tools/runqlen.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/runqlen.bt 2022-05-24 10:41:38.000000000 +0000 @@ -11,17 +11,19 @@ * 07-Oct-2018 Brendan Gregg Created this. */ +#ifndef BPFTRACE_HAVE_BTF #include // Until BTF is available, we'll need to declare some of this struct manually, // since it isn't available to be #included. This will need maintenance to match // your kernel version. It is from kernel/sched/sched.h: -struct cfs_rq_partial { +struct cfs_rq { struct load_weight load; unsigned long runnable_weight; unsigned int nr_running; unsigned int h_nr_running; }; +#endif BEGIN { @@ -31,7 +33,7 @@ profile:hz:99 { $task = (struct task_struct *)curtask; - $my_q = (struct cfs_rq_partial *)$task->se.cfs_rq; + $my_q = (struct cfs_rq *)$task->se.cfs_rq; $len = $my_q->nr_running; $len = $len > 0 ? $len - 1 : 0; // subtract currently running task @runqlen = lhist($len, 0, 100, 1); diff -Nru bpftrace-0.14.1/tools/tcpaccept.bt bpftrace-0.15.0/tools/tcpaccept.bt --- bpftrace-0.14.1/tools/tcpaccept.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/tcpaccept.bt 2022-05-24 10:41:38.000000000 +0000 @@ -16,8 +16,12 @@ * 23-Nov-2018 Dale Hamel created this. */ +#ifndef BPFTRACE_HAVE_BTF #include #include +#else +#include +#endif BEGIN { @@ -51,7 +55,7 @@ $qmax = $sk->sk_max_ack_backlog; // Destination port is big endian, it must be flipped - $dport = ($dport >> 8) | (($dport << 8) & 0x00FF00); + $dport = bswap($dport); time("%H:%M:%S "); printf("%-6d %-14s ", pid, comm); diff -Nru bpftrace-0.14.1/tools/tcpconnect.bt bpftrace-0.15.0/tools/tcpconnect.bt --- bpftrace-0.14.1/tools/tcpconnect.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/tcpconnect.bt 2022-05-24 10:41:38.000000000 +0000 @@ -19,8 +19,12 @@ * 23-Nov-2018 Dale Hamel created this. */ +#ifndef BPFTRACE_HAVE_BTF #include #include +#else +#include +#endif BEGIN { @@ -46,7 +50,7 @@ $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped - $dport = ($dport >> 8) | (($dport << 8) & 0x00FF00); + $dport = bswap($dport); time("%H:%M:%S "); printf("%-8d %-16s ", pid, comm); diff -Nru bpftrace-0.14.1/tools/tcpdrop.bt bpftrace-0.15.0/tools/tcpdrop.bt --- bpftrace-0.14.1/tools/tcpdrop.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/tcpdrop.bt 2022-05-24 10:41:38.000000000 +0000 @@ -11,14 +11,22 @@ * This provides information such as packet details, socket state, and kernel * stack trace for packets/segments that were dropped via tcp_drop(). + * WARNING: this script attaches to the tcp_drop kprobe which is likely inlined + * on newer kernels and not replaced by anything else, therefore + * the script will stop working + * Copyright (c) 2018 Dale Hamel. * Licensed under the Apache License, Version 2.0 (the "License") * 23-Nov-2018 Dale Hamel created this. */ +#ifndef BPFTRACE_HAVE_BTF #include #include +#else +#include +#endif BEGIN { @@ -57,7 +65,7 @@ $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped - $dport = ($dport >> 8) | (($dport << 8) & 0x00FF00); + $dport = bswap($dport); $state = $sk->__sk_common.skc_state; $statestr = @tcp_states[$state]; diff -Nru bpftrace-0.14.1/tools/tcplife.bt bpftrace-0.15.0/tools/tcplife.bt --- bpftrace-0.14.1/tools/tcplife.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/tcplife.bt 2022-05-24 10:41:38.000000000 +0000 @@ -13,10 +13,14 @@ * 17-Apr-2019 Brendan Gregg Created this. */ +#ifndef BPFTRACE_HAVE_BTF #include #include #include #include +#else +#include +#endif BEGIN { @@ -58,7 +62,7 @@ $delta_ms = (nsecs - @birth[$sk]) / 1e6; $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; - $dport = ($dport >> 8) | (($dport << 8) & 0xff00); + $dport = bswap($dport); $tp = (struct tcp_sock *)$sk; $pid = @skpid[$sk]; $comm = @skcomm[$sk]; diff -Nru bpftrace-0.14.1/tools/tcpretrans.bt bpftrace-0.15.0/tools/tcpretrans.bt --- bpftrace-0.14.1/tools/tcpretrans.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/tcpretrans.bt 2022-05-24 10:41:38.000000000 +0000 @@ -17,8 +17,12 @@ * 23-Nov-2018 Dale Hamel created this. */ +#ifndef BPFTRACE_HAVE_BTF #include #include +#else +#include +#endif BEGIN { @@ -63,7 +67,7 @@ $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped - $dport = ($dport >> 8) | (($dport << 8) & 0x00FF00); + $dport = bswap($dport); $state = $sk->__sk_common.skc_state; $statestr = @tcp_states[$state]; diff -Nru bpftrace-0.14.1/tools/tcpretrans_example.txt bpftrace-0.15.0/tools/tcpretrans_example.txt --- bpftrace-0.14.1/tools/tcpretrans_example.txt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/tcpretrans_example.txt 2022-05-24 10:41:38.000000000 +0000 @@ -5,10 +5,10 @@ retransmits. For example: # ./tcpretrans.bt -TIME PID LADDR:LPORT RADDR:RPORT STATE -00:43:54 0 10.229.20.82:46654 153.2.224.76:443 SYN_SENT -00:43:55 0 10.232.0.49:57678 10.229.20.99:24231 SYN_SENT -00:43:57 100 10.229.20.175:54224 10.201.76.122:443 ESTABLISHED +TIME PID LADDR:LPORT RADDR:RPORT STATE +01:55:05 0 10.153.223.157:22 69.53.245.40:34619 ESTABLISHED +01:55:05 0 10.153.223.157:22 69.53.245.40:34619 ESTABLISHED +01:55:17 0 10.153.223.157:22 69.53.245.40:22957 ESTABLISHED [...] This output shows three TCP retransmits, the first two were for an IPv4 diff -Nru bpftrace-0.14.1/tools/tcpsynbl.bt bpftrace-0.15.0/tools/tcpsynbl.bt --- bpftrace-0.14.1/tools/tcpsynbl.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/tcpsynbl.bt 2022-05-24 10:41:38.000000000 +0000 @@ -13,7 +13,9 @@ * 19-Apr-2019 Brendan Gregg Created this. */ +#ifndef BPFTRACE_HAVE_BTF #include +#endif BEGIN { diff -Nru bpftrace-0.14.1/tools/threadsnoop.bt bpftrace-0.15.0/tools/threadsnoop.bt --- bpftrace-0.14.1/tools/threadsnoop.bt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/threadsnoop.bt 2022-05-24 10:41:38.000000000 +0000 @@ -18,7 +18,7 @@ printf("%-10s %-6s %-16s %s\n", "TIME(ms)", "PID", "COMM", "FUNC"); } -uprobe:/lib/x86_64-linux-gnu/libpthread.so.0:pthread_create +uprobe:libpthread:pthread_create { printf("%-10u %-6d %-16s %s\n", elapsed / 1e6, pid, comm, usym(arg2)); diff -Nru bpftrace-0.14.1/tools/vfscount_example.txt bpftrace-0.15.0/tools/vfscount_example.txt --- bpftrace-0.14.1/tools/vfscount_example.txt 2021-12-29 17:05:34.000000000 +0000 +++ bpftrace-0.15.0/tools/vfscount_example.txt 2022-05-24 10:41:38.000000000 +0000 @@ -29,7 +29,7 @@ syscall interface. Tracing VFS calls gives you a high level breakdown of the kernel workload, and starting points for further investigation. -Notet that a warning was printed: "Warning: could not attach probe +Note that a warning was printed: "Warning: could not attach probe kprobe:vfs_dedupe_get_page.isra.21": these are not currently instrumentable by bpftrace/kprobes, so a warning is printed to let you know that they will be missed.